@@ -0,0 +1,511 @@
/**
* @file navigation_node.cpp
* @brief 导航节点 - 根据IMU数据进行惯性导航
*
* 功能:
* - 根据IMU加速度数据计算机器人位置( 双重积分)
* - 根据AHRS的heading计算机器人在场地坐标系中的朝向
* - 订阅机器人状态,根据状态决定导航策略
*
* 坐标系说明:
* - 场地坐标系:左下角为原点(0,0), X轴向右(0-12m), Y轴向上(0-8m)
* - IMU坐标系: 开始时X轴朝场地Y轴负方向, Y轴朝场地X轴负方向, Z轴朝地面
*/
# include <cmath>
# include <deque>
# include <eigen3/Eigen/Dense>
# include <geometry_msgs/msg/point.hpp>
# include <geometry_msgs/msg/vector3.hpp>
# include <rclcpp/rclcpp.hpp>
# include <sensor_msgs/msg/imu.hpp>
# include <std_msgs/msg/float64.hpp>
# include <std_msgs/msg/int32.hpp>
// 地图配置
constexpr double MAP_WIDTH = 12.0 ; // 场地宽度 (X方向) 单位:米
constexpr double MAP_HEIGHT = 8.0 ; // 场地高度 (Y方向) 单位:米
constexpr double ROBOT_RADIUS = 0.375 ; // 机器人半径 0.75m直径
// 初始位置
constexpr double INITIAL_X = 1.235 ;
constexpr double INITIAL_Y = 6.265 ;
// 障碍区定义
struct Rectangle {
double x1 , y1 , x2 , y2 ;
} ;
constexpr Rectangle OBSTACLE1 = { 2.5 , 1.5 , 4.5 , 8.0 } ;
constexpr Rectangle OBSTACLE2 = { 7.5 , 0.0 , 9.5 , 6.5 } ;
// 我方基地
constexpr Rectangle HOME_BASE = { 0.0 , 6.0 , 1.5 , 8.0 } ;
// 增益点
constexpr Rectangle BUFF_ZONE = { 4.5 , 2.5 , 7.5 , 5.5 } ;
// 机器人状态
enum class RobotState {
WAITING = 0 , // 等待
MOVING_TO_POINT , // 进点
SEARCHING , // 索敌
ATTACKING , // 攻击
RETURNING_HOME // 回家
} ;
// 简单低通滤波器
class LowPassFilter {
public :
LowPassFilter ( double alpha = 0.1 ) : alpha_ ( alpha ) , initialized_ ( false ) { }
double filter ( double value ) {
if ( ! initialized_ ) {
last_value_ = value ;
initialized_ = true ;
return value ;
}
last_value_ = alpha_ * value + ( 1.0 - alpha_ ) * last_value_ ;
return last_value_ ;
}
void reset ( ) { initialized_ = false ; }
private :
double alpha_ ;
double last_value_ ;
bool initialized_ ;
} ;
// 滑动窗口平均滤波器
class MovingAverageFilter {
public :
explicit MovingAverageFilter ( size_t window_size = 10 )
: window_size_ ( window_size ) { }
double filter ( double value ) {
buffer_ . push_back ( value ) ;
if ( buffer_ . size ( ) > window_size_ ) {
buffer_ . pop_front ( ) ;
}
if ( buffer_ . empty ( ) )
return value ;
double sum = 0.0 ;
for ( double v : buffer_ ) {
sum + = v ;
}
return sum / buffer_ . size ( ) ;
}
void reset ( ) { buffer_ . clear ( ) ; }
private :
size_t window_size_ ;
std : : deque < double > buffer_ ;
} ;
class NavigationNode : public rclcpp : : Node {
public :
NavigationNode ( ) : Node ( " navigation_node " ) {
// 声明参数
this - > declare_parameter ( " initial_x " , INITIAL_X ) ;
this - > declare_parameter ( " initial_y " , INITIAL_Y ) ;
this - > declare_parameter ( " velocity_decay " , 0.95 ) ; // 速度衰减系数(零速校正)
this - > declare_parameter ( " position_correction " , 0.001 ) ; // 位置漂移校正
this - > declare_parameter ( " static_threshold " , 0.05 ) ; // 静止检测阈值 (m/s²)
this - > declare_parameter ( " calibration_samples " , 100 ) ; // 初始校准采样数
this - > declare_parameter ( " acc_lpf_alpha " , 0.1 ) ; // 加速度低通滤波系数
this - > declare_parameter ( " vel_lpf_alpha " , 0.2 ) ; // 速度低通滤波系数
// 获取参数
pos_x_ = this - > get_parameter ( " initial_x " ) . as_double ( ) ;
pos_y_ = this - > get_parameter ( " initial_y " ) . as_double ( ) ;
velocity_decay_ = this - > get_parameter ( " velocity_decay " ) . as_double ( ) ;
position_correction_ =
this - > get_parameter ( " position_correction " ) . as_double ( ) ;
static_threshold_ = this - > get_parameter ( " static_threshold " ) . as_double ( ) ;
calibration_samples_ = this - > get_parameter ( " calibration_samples " ) . as_int ( ) ;
acc_lpf_alpha_ = this - > get_parameter ( " acc_lpf_alpha " ) . as_double ( ) ;
vel_lpf_alpha_ = this - > get_parameter ( " vel_lpf_alpha " ) . as_double ( ) ;
// 初始化滤波器
acc_filter_x_ . reset ( new LowPassFilter ( acc_lpf_alpha_ ) ) ;
acc_filter_y_ . reset ( new LowPassFilter ( acc_lpf_alpha_ ) ) ;
vel_filter_x_ . reset ( new LowPassFilter ( vel_lpf_alpha_ ) ) ;
vel_filter_y_ . reset ( new LowPassFilter ( vel_lpf_alpha_ ) ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " ======================================== " ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " 导航节点初始化 " ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " 初始位置: (%.3f, %.3f) " , pos_x_ , pos_y_ ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " 速度衰减系数: %.3f " , velocity_decay_ ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " 静止检测阈值: %.3f m/s² " ,
static_threshold_ ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " 校准采样数: %d " , calibration_samples_ ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " ======================================== " ) ;
// 创建订阅者
imu_sub_ = this - > create_subscription < sensor_msgs : : msg : : Imu > (
" imu/data " , 10 ,
std : : bind ( & NavigationNode : : imuCallback , this , std : : placeholders : : _1 ) ) ;
state_sub_ = this - > create_subscription < std_msgs : : msg : : Int32 > (
" robot_state " , 10 ,
std : : bind ( & NavigationNode : : stateCallback , this , std : : placeholders : : _1 ) ) ;
// 创建发布者
pose_pub_ =
this - > create_publisher < geometry_msgs : : msg : : Point > ( " robot_pose " , 10 ) ;
velocity_pub_ = this - > create_publisher < geometry_msgs : : msg : : Vector3 > (
" robot_velocity " , 10 ) ;
heading_pub_ =
this - > create_publisher < std_msgs : : msg : : Float64 > ( " robot_heading " , 10 ) ;
nav_cmd_pub_ =
this - > create_publisher < geometry_msgs : : msg : : Vector3 > ( " nav_cmd " , 10 ) ;
// 创建定时器,用于定期发布导航信息
timer_ = this - > create_wall_timer (
std : : chrono : : milliseconds ( 20 ) , // 50Hz
std : : bind ( & NavigationNode : : navigationLoop , this ) ) ;
RCLCPP_INFO ( this - > get_logger ( ) , " 导航节点初始化完成 " ) ;
}
private :
// IMU数据回调
void imuCallback ( const sensor_msgs : : msg : : Imu : : SharedPtr msg ) {
// 提取四元数 (ROS格式: x, y, z, w)
double qx = msg - > orientation . x ;
double qy = msg - > orientation . y ;
double qz = msg - > orientation . z ;
double qw = msg - > orientation . w ;
// 将四元数转换为欧拉角 (roll, pitch, yaw)
// yaw: 绕Z轴旋转 (heading)
double siny_cosp = 2.0 * ( qw * qz + qx * qy ) ;
double cosy_cosp = 1.0 - 2.0 * ( qy * qy + qz * qz ) ;
double yaw = std : : atan2 ( siny_cosp , cosy_cosp ) ;
// roll: 绕X轴旋转
double sinr_cosp = 2.0 * ( qw * qx + qy * qz ) ;
double cosr_cosp = 1.0 - 2.0 * ( qx * qx + qy * qy ) ;
double roll = std : : atan2 ( sinr_cosp , cosr_cosp ) ;
// pitch: 绕Y轴旋转
double sinp = 2.0 * ( qw * qy - qz * qx ) ;
double pitch = std : : asin ( std : : clamp ( sinp , - 1.0 , 1.0 ) ) ;
// 保存姿态数据
current_roll_ = roll ;
current_pitch_ = pitch ;
current_heading_ = yaw ; // 这是IMU坐标系下的heading
// 初始校准阶段: 收集初始heading样本
if ( is_calibrating_ ) {
heading_samples_ . push_back ( yaw ) ;
if ( heading_samples_ . size ( ) > =
static_cast < size_t > ( calibration_samples_ ) ) {
// 计算平均初始heading
double sum = 0.0 ;
for ( double h : heading_samples_ ) {
sum + = h ;
}
initial_heading_ = sum / heading_samples_ . size ( ) ;
is_calibrating_ = false ;
RCLCPP_INFO ( this - > get_logger ( ) ,
" 初始校准完成! 初始heading: %.4f rad (%.2f°) " ,
initial_heading_ , initial_heading_ * 180.0 / M_PI ) ;
}
return ;
}
// 计算场地坐标系下的朝向
// 初始时IMU X轴朝场地Y轴负方向, 即heading = -PI/2 对应场地朝向0
// 需要建立IMU heading与场地朝向的映射关系
// 计算相对于初始朝向的变化
double heading_delta = normalizeAngle ( yaw - initial_heading_ ) ;
// 场地朝向: 初始时机器人朝场地Y轴负方向( 朝向下方)
// 当IMU顺时针旋转( yaw增大) , 场地朝向顺时针变化
field_heading_ = normalizeAngle ( - M_PI_2 + heading_delta ) ;
// 提取加速度 (IMU坐标系)
double acc_x_imu = msg - > linear_acceleration . x ;
double acc_y_imu = msg - > linear_acceleration . y ;
// 应用低通滤波
acc_x_imu = acc_filter_x_ - > filter ( acc_x_imu ) ;
acc_y_imu = acc_filter_y_ - > filter ( acc_y_imu ) ;
// 静止检测:加速度小于阈值时认为是静止状态
double acc_magnitude =
std : : sqrt ( acc_x_imu * acc_x_imu + acc_y_imu * acc_y_imu ) ;
is_static_ = acc_magnitude < static_threshold_ ;
// 将加速度从IMU坐标系转换到场地坐标系
// IMU坐标系到场地坐标系的旋转:
// - 初始时IMU X轴朝场地-Y方向, IMU Y轴朝场地-X方向
// - 需要先将加速度旋转到与场地坐标系对齐
// 旋转矩阵: 从IMU坐标系到场地坐标系
// 机器人朝向field_heading_时, 需要将IMU加速度旋转
double cos_h = std : : cos ( field_heading_ ) ;
double sin_h = std : : sin ( field_heading_ ) ;
// IMU X轴朝机器人前方, Y轴朝机器人左方
// 场地坐标系: X向右, Y向上
// 机器人前方对应场地坐标系的(cos(field_heading_), sin(field_heading_))
// 机器人左方对应场地坐标系的(-sin(field_heading_), cos(field_heading_))
// 注意: IMU的X轴是机器人前方, Y轴是左方
// 场地坐标系转换:前方分量映射到场地朝向,左方分量垂直于场地朝向
acc_field_x_ = acc_x_imu * cos_h - acc_y_imu * sin_h ;
acc_field_y_ = acc_x_imu * sin_h + acc_y_imu * cos_h ;
// 获取当前时间
rclcpp : : Time current_time = msg - > header . stamp ;
if ( ! last_time_ . nanoseconds ( ) ) {
last_time_ = current_time ;
return ;
}
// 计算时间差
double dt = ( current_time - last_time_ ) . seconds ( ) ;
last_time_ = current_time ;
if ( dt < = 0 | | dt > 0.1 ) { // 时间差异常,跳过本次计算
return ;
}
// 速度积分(使用梯形积分)
vel_x_ + = ( acc_field_x_ + last_acc_field_x_ ) * 0.5 * dt ;
vel_y_ + = ( acc_field_y_ + last_acc_field_y_ ) * 0.5 * dt ;
// 保存当前加速度用于下次积分
last_acc_field_x_ = acc_field_x_ ;
last_acc_field_y_ = acc_field_y_ ;
// 零速校正:如果检测到静止,逐渐减小速度
if ( is_static_ ) {
vel_x_ * = velocity_decay_ ;
vel_y_ * = velocity_decay_ ;
// 如果速度很小,直接置零
if ( std : : abs ( vel_x_ ) < 0.01 )
vel_x_ = 0.0 ;
if ( std : : abs ( vel_y_ ) < 0.01 )
vel_y_ = 0.0 ;
}
// 应用速度滤波
vel_x_ = vel_filter_x_ - > filter ( vel_x_ ) ;
vel_y_ = vel_filter_y_ - > filter ( vel_y_ ) ;
// 位置积分(使用梯形积分)
pos_x_ + = vel_x_ * dt ;
pos_y_ + = vel_y_ * dt ;
// 边界检查:确保机器人在场地范围内
pos_x_ = std : : clamp ( pos_x_ , ROBOT_RADIUS , MAP_WIDTH - ROBOT_RADIUS ) ;
pos_y_ = std : : clamp ( pos_y_ , ROBOT_RADIUS , MAP_HEIGHT - ROBOT_RADIUS ) ;
has_imu_data_ = true ;
}
// 状态回调
void stateCallback ( const std_msgs : : msg : : Int32 : : SharedPtr msg ) {
current_state_ = static_cast < RobotState > ( msg - > data ) ;
// 根据状态更新目标位置
switch ( current_state_ ) {
case RobotState : : WAITING :
// 等待状态,不需要移动
break ;
case RobotState : : MOVING_TO_POINT :
// 进点:目标为增益点中心
target_x_ = ( BUFF_ZONE . x1 + BUFF_ZONE . x2 ) / 2.0 ;
target_y_ = ( BUFF_ZONE . y1 + BUFF_ZONE . y2 ) / 2.0 ;
break ;
case RobotState : : SEARCHING :
// 索敌:在增益点附近巡逻
target_x_ = ( BUFF_ZONE . x1 + BUFF_ZONE . x2 ) / 2.0 ;
target_y_ = ( BUFF_ZONE . y1 + BUFF_ZONE . y2 ) / 2.0 ;
break ;
case RobotState : : ATTACKING :
// 攻击:保持在当前位置或轻微调整
break ;
case RobotState : : RETURNING_HOME :
// 回家:目标为我方基地中心
target_x_ = ( HOME_BASE . x1 + HOME_BASE . x2 ) / 2.0 ;
target_y_ = ( HOME_BASE . y1 + HOME_BASE . y2 ) / 2.0 ;
break ;
}
}
// 导航主循环
void navigationLoop ( ) {
if ( ! has_imu_data_ ) {
return ;
}
// 发布机器人位置 (使用Point, z表示heading)
auto pose_msg = geometry_msgs : : msg : : Point ( ) ;
pose_msg . x = pos_x_ ;
pose_msg . y = pos_y_ ;
pose_msg . z = field_heading_ ; // 借用z字段存储heading
pose_pub_ - > publish ( pose_msg ) ;
// 发布速度
auto vel_msg = geometry_msgs : : msg : : Vector3 ( ) ;
vel_msg . x = vel_x_ ;
vel_msg . y = vel_y_ ;
vel_msg . z = 0.0 ; // 平面运动, Z向速度为0
velocity_pub_ - > publish ( vel_msg ) ;
// 发布朝向
auto heading_msg = std_msgs : : msg : : Float64 ( ) ;
heading_msg . data = field_heading_ ;
heading_pub_ - > publish ( heading_msg ) ;
// 计算导航指令(简单的位置控制)
geometry_msgs : : msg : : Vector3 nav_cmd ;
if ( current_state_ = = RobotState : : WAITING ) {
// 等待状态:不移动
nav_cmd . x = 0.0 ; // 前进速度
nav_cmd . y = 0.0 ; // 横向速度
nav_cmd . z = 0.0 ; // 旋转速度
} else {
// 计算到目标点的向量
double dx = target_x_ - pos_x_ ;
double dy = target_y_ - pos_y_ ;
double distance = std : : sqrt ( dx * dx + dy * dy ) ;
// 计算目标方向在场地坐标系中的角度
double target_angle = std : : atan2 ( dy , dx ) ;
// 计算需要旋转的角度(目标方向与当前朝向的差)
double angle_diff = normalizeAngle ( target_angle - field_heading_ ) ;
// 简单的导航控制
// 如果距离目标较远,先朝向目标再前进
if ( distance > 0.3 ) { // 30cm阈值
// 旋转控制:将角度差转换为旋转速度
double max_rot_speed = 1.0 ; // 最大旋转速度 rad/s
nav_cmd . z = std : : clamp ( angle_diff * 2.0 , - max_rot_speed , max_rot_speed ) ;
// 前进控制:当朝向接近目标方向时前进
if ( std : : abs ( angle_diff ) < 0.3 ) { // 约17度以内
double max_speed = 1.0 ; // 最大前进速度 m/s
nav_cmd . x = std : : min ( distance * 2.0 , max_speed ) ;
} else {
nav_cmd . x = 0.0 ; // 先转向
}
nav_cmd . y = 0.0 ; // 暂时不使用横向移动
} else {
// 到达目标附近,停止
nav_cmd . x = 0.0 ;
nav_cmd . y = 0.0 ;
nav_cmd . z = 0.0 ;
}
}
nav_cmd_pub_ - > publish ( nav_cmd ) ;
// 日志输出(降低频率)
static int log_counter = 0 ;
if ( + + log_counter > = 10 ) {
log_counter = 0 ;
RCLCPP_INFO ( this - > get_logger ( ) ,
" Pos: (%.3f, %.3f) | Heading: %.2f° | Vel: (%.3f, %.3f) | "
" State: %d | Static: %s " ,
pos_x_ , pos_y_ , field_heading_ * 180.0 / M_PI , vel_x_ , vel_y_ ,
static_cast < int > ( current_state_ ) , is_static_ ? " Yes " : " No " ) ;
}
}
// 角度归一化到 [-PI, PI]
double normalizeAngle ( double angle ) {
while ( angle > M_PI )
angle - = 2.0 * M_PI ;
while ( angle < - M_PI )
angle + = 2.0 * M_PI ;
return angle ;
}
// 订阅者
rclcpp : : Subscription < sensor_msgs : : msg : : Imu > : : SharedPtr imu_sub_ ;
rclcpp : : Subscription < std_msgs : : msg : : Int32 > : : SharedPtr state_sub_ ;
// 发布者
rclcpp : : Publisher < geometry_msgs : : msg : : Point > : : SharedPtr pose_pub_ ;
rclcpp : : Publisher < geometry_msgs : : msg : : Vector3 > : : SharedPtr velocity_pub_ ;
rclcpp : : Publisher < std_msgs : : msg : : Float64 > : : SharedPtr heading_pub_ ;
rclcpp : : Publisher < geometry_msgs : : msg : : Vector3 > : : SharedPtr nav_cmd_pub_ ;
// 定时器
rclcpp : : TimerBase : : SharedPtr timer_ ;
// 状态
RobotState current_state_ = RobotState : : WAITING ;
// 位置 (场地坐标系)
double pos_x_ = INITIAL_X ;
double pos_y_ = INITIAL_Y ;
// 速度 (场地坐标系)
double vel_x_ = 0.0 ;
double vel_y_ = 0.0 ;
// 加速度 (场地坐标系)
double acc_field_x_ = 0.0 ;
double acc_field_y_ = 0.0 ;
double last_acc_field_x_ = 0.0 ;
double last_acc_field_y_ = 0.0 ;
// 姿态
double current_roll_ = 0.0 ;
double current_pitch_ = 0.0 ;
double current_heading_ = 0.0 ; // IMU坐标系下的heading
double field_heading_ = - M_PI_2 ; // 场地坐标系下的朝向( 初始朝Y轴负方向)
double initial_heading_ = 0.0 ; // 初始heading( 用于校准)
// 目标位置
double target_x_ = 0.0 ;
double target_y_ = 0.0 ;
// 滤波器
std : : unique_ptr < LowPassFilter > acc_filter_x_ ;
std : : unique_ptr < LowPassFilter > acc_filter_y_ ;
std : : unique_ptr < LowPassFilter > vel_filter_x_ ;
std : : unique_ptr < LowPassFilter > vel_filter_y_ ;
// 校准
std : : vector < double > heading_samples_ ;
bool is_calibrating_ = true ;
int calibration_samples_ = 100 ;
// 参数
double velocity_decay_ = 0.95 ;
double position_correction_ = 0.001 ;
double static_threshold_ = 0.05 ;
double acc_lpf_alpha_ = 0.1 ;
double vel_lpf_alpha_ = 0.2 ;
// 时间
rclcpp : : Time last_time_ ;
// 标志
bool has_imu_data_ = false ;
bool is_static_ = false ;
} ;
int main ( int argc , char * argv [ ] ) {
rclcpp : : init ( argc , argv ) ;
auto node = std : : make_shared < NavigationNode > ( ) ;
rclcpp : : spin ( node ) ;
rclcpp : : shutdown ( ) ;
return 0 ;
}