2025-10-09
数据结构与算法
00
2025-10-09
数据结构与算法
00
2025-10-07
计算机学习
00

实现功能:

对于给定的由可见字符和空格组成的字符串,按照下方的规则进行排序:

  • 按照字母表中的顺
2025-09-23
计算机学习
00

状态机(Finite State Machine, FSM) 是一种计算模型,用来描述系统在不同时间点所处的不同状态,以及在这些状态之间如何转换。

基本概念

  • 状态(State):系统在某一时刻的特定情况或模式
  • 事件(Event):触发状态转换的条件或输入
  • 转换(Transition):从一个状态到另一个状态的过程
  • 动作(Action):在状态转换时执行的操作

文件结构说明

  • 头文件: ego_replan_fsm.h - 包含类的声明、枚举定义、成员变量声明等
  • 源文件: ego_replan_fsm.cpp - 包含类的具体实现

ego_replan_fsm.h 中

  • FSM_EXEC_STATE 枚举 - 有限状态机执行状态
c
展开代码
private: /* ---------- flag ---------- */ enum FSM_EXEC_STATE { INIT, // 初始状态 WAIT_TARGET, // 等待目标 GEN_NEW_TRAJ, // 生成新轨迹 REPLAN_TRAJ, // 重新规划轨迹 EXEC_TRAJ, // 执行轨迹 EMERGENCY_STOP // 紧急停止 };

这个枚举定义了无人机路径规划系统的6个核心状态

TARGET_TYPE 枚举 - 目标类型

  • TARGET_TYPE 枚举 - 目标类型
c
展开代码
enum TARGET_TYPE { MANUAL_TARGET = 1, // 手动选择目标 PRESET_TARGET = 2, // 预设目标 REFENCE_PATH = 3 // 参考路径 };

这个枚举定义了3种目标设置方式

2025-09-23
计算机学习
00

::的含义:从“命名空间”或“类”这个蓝图里拿东西

在C++中,有两个主要的“盒子”:

1.命名空间 (Namespace):一个防止名字冲突的大盒子

  • 从“命名空间”这个盒子里拿东西

不同库的作者可能定义了同名的函数。比如,一个数学库和一个物理库可能都有 calculate()函数。用 Math::calculate()和 Physics::calculate()就能区分开。

例:

std::cout:意思是“我要使用 std这个命名空间里的 cout功能”。

bash
展开代码
ros::NodeHandle nh; // 从ros命名空间里找到NodeHandle这个类 ros::Publisher pub; // 从ros命名空间里找到Publisher这个类

这里的 ros::就是在说:“编译器你好,我要用的 NodeHandle和 Publisher是ROS库提供的,它们放在一个叫 ros的大盒子里。”

2.类 (Class):一个包含数据和功能的蓝图盒子

  • 从“类”这个盒子里拿东西(访问静态成员)

类就像一个蓝图,比如一个“汽车”的蓝图。这个蓝图里规定了所有汽车都有“轮子数量”这个属性(值为4)和一个“鸣笛”的功能。

有些属性和功能是属于蓝图本身的,而不是属于某辆具体的汽车。比如“轮子数量”,所有汽车都一样,是蓝图固有的。这种属于蓝图(类本身)的东西,就用 ::来访问。

例:

Car::numberOfWheels:意思是“从 Car这个类(蓝图)本身获取 numberOfWheels的值”。

Car::honk():意思是“调用 Car这个类(蓝图)本身的 honk函数”。

.的含义:从“对象”(根据蓝图造出来的具体东西)里拿东西

例:

  • nh.param()
  • myCar.drive() 左边必须是一个具体的对象(变量)。这个对象必须是已经创建好的实例。
bash
展开代码
ros::NodeHandle nh nh.param(...)
  • 1.ros::NodeHandle:使用 ::告诉编译器,“NodeHandle这个类型在 ros命名空间里”
  • 2.nh:根据这个类型,创建了一个名为 nh的具体对象。
  • 3.nh.param(...):这行代码是让你创建好的具体对象 nh,去调用它的成员函数 param

ego-planner traj_server.cpp

访问命名空间 (Namespace) 中的内容

ros命名空间访问

  • ros::NodeHandle node;

从 ros命名空间中访问 NodeHandle类

  • ros::Publisher pos_cmd_pub;;

从 ros命名空间中访问 Publisher类,用于发布消息到ROS话题

  • ros::Subscriber bspline_sub = ...

从 ros命名空间中访问 Subscriber类

  • ros::Timer cmd_timer = ...

从 ros命名空间中访问 Timer类

  • ros::Time time_now = ros::Time::now();

ros::Time: 从 ros命名空间访问 Time类

ros::Time::now(): 访问 Time类的静态成员函数 now()。

  • ros::Duration(1.0).sleep();

ros::Duration: 从 ros命名空间访问 Duration类

  • quadrotor_msgs::PositionCommand cmd;

从 quadrotor_msgs命名空间中访问 PositionCommand类(这是一个消息类型)。

  • Eigen::Vector3d pos(Eigen::Vector3d::Zero());

Eigen::Vector3d: 从 Eigen命名空间访问 Vector3d类

Eigen::Vector3d::Zero(): 访问 Vector3d类的静态成员函数Zero()

消息类型命名空间访问

  • quadrotor_msgs::PositionCommand cmd;

quadrotor_msgs:: 指定使用四旋翼消息包中的PositionCommand类型

  • void bsplineCallback(ego_planner::BsplineConstPtr msg)

ego_planner:: 指定使用EGO规划器包中的Bspline消息类型

访问类的静态成员 (Static Members)

  • ros::Time::now()

调用 ros::Time这个类本身的静态成员函数 now()。你不需要一个 Time对象实例就能调用它,它返回当前时间。

  • Eigen::Vector3d::Zero()

调用 Eigen::Vector3d这个类本身的静态成员函数 Zero()。它返回一个所有分量都为0的Vector3d对象。

ros::Time time_now;(创建对象) -> time_now.toSec();(使用对象的方法)

• 这里先用 ::指定类型,然后用 .调用具体对象的方法。

ros::Time::now();(直接调用类的功能)

• 这里直接用 ::调用属于类本身的静态方法,不需要先创建对象。

订阅和发布:实现数据的流向

发布者的创建

cpp
展开代码
// 创建发布者的标准格式 ros::Publisher pub = node_handle.advertise<MessageType>(topic_name, queue_size);
cpp
展开代码
pos_cmd_pub = node.advertise<quadrotor_msgs::PositionCommand>("/position_cmd", 50);
  • .advertise(): 这是节点句柄的一个方法。调用它意味着向ROS系统宣告:“我这个节点想要发布消息!”
  • <quadrotor_msgs::PositionCommand>: 这是一个模板参数,指定了你要发布的消息的类型。编译器会根据这个类型来检查你后续publish的数据格式是否正确。这里指定的是自定义消息类型 quadrotor_msgs/PositionCommand
  • "/position_cmd": 这是一个字符串参数,指定了你要发布的话题的名称。其他节点将通过订阅这个名字来接收你的消息。以/开头表示这是全局名称。
  • 50: 这是发布队列的大小。它是一个缓冲区,用于缓存即将发送的消息。

为什么需要队列?如果消息发布得非常快,而网络或接收节点处理得慢,新的消息就会进入这个队列排队等待发送。

  • 返回值: .advertise()方法返回一个 ros::Publisher对象(这里赋值给 pos_cmd_pub)。这个对象就是你的“发布工具”,后续用它来实际发送消息。

使用发布者

创建好后,通常在循环或回调函数中使用它来发送数据:

cpp
展开代码
pos_cmd_pub.publish(cmd); // cmd 必须是 quadrotor_msgs::PositionCommand 类型的对象

订阅者的创建

订阅者的作用是监听一个特定的话题(Topic),并在收到消息时自动调用一个函数来处理它。

cpp
展开代码
// 创建订阅者的标准格式 ros::Subscriber sub = node_handle.subscribe(topic_name, queue_size, callback_function);
cpp
展开代码
ros::Subscriber bspline_sub = node.subscribe("planning/bspline", 10, bsplineCallback);

监听名为 planning/bspline的话题。每当有新的消息发布到该话题上,就去调用我指定的 bsplineCallback函数,并把消息作为参数传给它

  • .subscribe(): 节点句柄的方法。调用它意味着向ROS系统宣告:“我这个节点想要订阅某个话题的消息!”
  • bsplineCallback: 这是最重要的参数——回调函数。它必须是一个函数指针(或函数对象)。当有新消息到达时,ROS会自动调用这个函数,并将消息作为参数传递给它。

bsplineCallback函数:

cpp
展开代码
void bsplineCallback(ego_planner::BsplineConstPtr msg)

触发条件:当轨迹服务器接收到来自规划器的B样条轨迹消息时自动调用

回调函数bsplineCallback的分析

  • 输入消息结构分析

根据 Bspline.msg 消息定义:

msg
展开代码
int32 order # B样条阶数 int64 traj_id # 轨迹ID time start_time # 开始时间 float64[] knots # 节点向量 geometry_msgs/Point[] pos_pts # 位置控制点 float64[] yaw_pts # 偏航角控制点 float64 yaw_dt # 偏航角时间间隔
text
展开代码
ROS消息 (ego_planner::Bspline) ↓ 解析控制点 → Eigen::MatrixXd pos_pts ↓ 解析节点向量 → Eigen::VectorXd knots ↓ 构建B样条对象 → UniformBspline pos_traj ↓ 计算导数 → traj_[0,1,2] (位置、速度、加速度) //traj_[0]:位置轨迹(原始B样条) traj_[1]:速度轨迹(一阶导数) traj_[2]:加速度轨迹(二阶导数) ↓ 设置元信息 → start_time_, traj_id_, traj_duration_ ↓ 标记状态 → receive_traj_ = true //通知系统已接收到有效轨迹,可以开始执行