网上关于ROS2的基础教程非常多,在此,我给各位初学者分享一个最基础案例,让各位更熟悉ros2的使用,和应用场景。考虑到部分人对cpp不熟悉,我会逐步解释一些细节之处。
环境条件:liunx下的vscode编译器。
ROS2安装方法:wget http://fishros.com/install -O fishros && . fishros(来源于鱼香ROS的源码)
首先,ros2 run turtlesim turtlesim_node 打开小乌龟 (ROS2 安装好自带)
然后创造一个这样的工作文件夹(这里建议手动构建,可以让你更加理解整个编译过程)
turtle_control_pkg/
├── src/
│ ├── turtle_controller.cpp # 控制节点
│ ├── pose_monitor.cpp # 监控节点
│ └── turtle_drawer.cpp # 绘图节点
├── include/
├── package.xml
└── CMakeLists.txt
首先是turtle_controller.cpp
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "turtlesim/msg/pose.hpp"
#include <chrono>
#include <memory>
#include <cmath>
using namespace std::chrono_literals;
class TurtleController : public rclcpp::Node
{
public:
TurtleController() : Node("turtle_controller")
{
// 创建发布者,发布速度命令
cmd_publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10);
// 创建订阅者,订阅位置信息
pose_subscriber_ = this->create_subscription<turtlesim::msg::Pose>(
"/turtle1/pose", 10,
std::bind(&TurtleController::pose_callback, this, std::placeholders::_1));
// 创建定时器,定期发布控制命令
timer_ = this->create_wall_timer(100ms, std::bind(&TurtleController::timer_callback, this));
// 初始化变量
start_time_ = this->now();
mode_ = 0; // 控制模式
RCLCPP_INFO(this->get_logger(), "Turtle Controller 已启动!");
}
private:
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr cmd_publisher_;
rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr pose_subscriber_;
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Time start_time_;
int mode_;
double current_x_ = 0.0;
double current_y_ = 0.0;
void pose_callback(const turtlesim::msg::Pose::SharedPtr msg)
{
current_x_ = msg->x;
current_y_ = msg->y;
// 每5秒打印一次位置信息
auto now = this->now();
if ((now - start_time_).seconds() > 5.0) {
RCLCPP_INFO(this->get_logger(), "海龟位置: (%.2f, %.2f), 朝向: %.2f",
current_x_, current_y_, msg->theta);
start_time_ = now;
}
}
void timer_callback()
{
auto twist_msg = geometry_msgs::msg::Twist();
auto now = this->now();
double elapsed_time = (now - start_time_).seconds();
// 每10秒切换一种运动模式
if (elapsed_time > 10.0) {
mode_ = (mode_ + 1) % 4;
start_time_ = now;
RCLCPP_INFO(this->get_logger(), "切换模式: %d", mode_);
}
// 根据模式设置不同的运动方式
switch (mode_) {
case 0: // 转圈
twist_msg.linear.x = 2.0;
twist_msg.angular.z = 1.0;
break;
case 1: // 前进后退
twist_msg.linear.x = 2.0 * sin(elapsed_time * 2.0);
break;
case 2: // 画正方形
if (fmod(elapsed_time, 2.0) < 1.0) {
twist_msg.linear.x = 1.5;
twist_msg.angular.z = 0.0;
} else {
twist_msg.linear.x = 0.0;
twist_msg.angular.z = 1.57; // 90度
}
break;
case 3: // 螺旋运动
twist_msg.linear.x = 1.0 + elapsed_time * 0.1;
twist_msg.angular.z = 1.0;
break;
}
cmd_publisher_->publish(twist_msg);
}
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<TurtleController>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
1.using namespace std::chrono_literals :为了创建定时器可以给数值带上单位例如 100ms 10s
2.TurtleController() : Node("turtle_controller"):初始化类节点的名称为“turtle_controller”
3.cmd_publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10);
在这里<geometry_msgs::msg::Twist>的有意思是 我要创建一个<geometry_msgs::msg::Twist>类型的消息,/turtle1/cmd_vel是小乌龟创建时的默认话题 ,你可以 ros2 node info /turtlesim进行查看,10是队列大小,(说白了就是几条消息),后续你创建自己的机器人的时候,也可以设置自己机器人的话题。
接着是pose_monitor.cpp
#include "rclcpp/rclcpp.hpp"
#include "turtlesim/msg/pose.hpp"
#include <cmath>
class PoseMonitor : public rclcpp::Node
{
public:
PoseMonitor() : Node("pose_monitor")
{
// 创建订阅者,订阅海龟位置
pose_subscriber_ = this->create_subscription<turtlesim::msg::Pose>(
"/turtle1/pose", 10,
std::bind(&PoseMonitor::pose_callback, this, std::placeholders::_1));
// 创建定时器,定期报告统计信息
timer_ = this->create_wall_timer(3s, std::bind(&PoseMonitor::timer_callback, this));
RCLCPP_INFO(this->get_logger(), "位置监控器已启动!");
}
private:
rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr pose_subscriber_;
rclcpp::TimerBase::SharedPtr timer_;
double min_x_ = 11.0, max_x_ = 0.0;
double min_y_ = 11.0, max_y_ = 0.0;
int pose_count_ = 0;
void pose_callback(const turtlesim::msg::Pose::SharedPtr msg)
{
pose_count_++;
// 更新边界
if (msg->x < min_x_) min_x_ = msg->x;
if (msg->x > max_x_) max_x_ = msg->x;
if (msg->y < min_y_) min_y_ = msg->y;
if (msg->y > max_y_) max_y_ = msg->y;
// 检查是否接近边界
if (msg->x < 0.5 || msg->x > 10.5 || msg->y < 0.5 || msg->y > 10.5) {
RCLCPP_WARN(this->get_logger(), "海龟接近边界! 当前位置: (%.2f, %.2f)", msg->x, msg->y);
}
}
void timer_callback()
{
double area_width = max_x_ - min_x_;
double area_height = max_y_ - min_y_;
RCLCPP_INFO(this->get_logger(), "=== 位置监控报告 ===");
RCLCPP_INFO(this->get_logger(), "接收到的位置消息: %d 条", pose_count_);
RCLCPP_INFO(this->get_logger(), "活动范围: X[%.2f, %.2f], Y[%.2f, %.2f]",
min_x_, max_x_, min_y_, max_y_);
RCLCPP_INFO(this->get_logger(), "活动区域大小: %.2f x %.2f", area_width, area_height);
RCLCPP_INFO(this->get_logger(), "====================");
}
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<PoseMonitor>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
再接着是turtle_drawer.cpp
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include <chrono>
#include <vector>
#include <cmath>
using namespace std::chrono_literals;
class TurtleDrawer : public rclcpp::Node
{
public:
TurtleDrawer() : Node("turtle_drawer")
{
// 创建发布者
cmd_publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10);
// 创建定时器
timer_ = this->create_wall_timer(500ms, std::bind(&TurtleDrawer::timer_callback, this));
// 初始化绘图指令
init_drawing_commands();
RCLCPP_INFO(this->get_logger(), "绘图节点已启动! 将绘制一个五角星");
}
private:
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr cmd_publisher_;
rclcpp::TimerBase::SharedPtr timer_;
std::vector<geometry_msgs::msg::Twist> commands_;
size_t current_command_ = 0;
int step_counter_ = 0;
void init_drawing_commands()
{
// 绘制五角星的命令序列
geometry_msgs::msg::Twist cmd;
// 移动到起始位置
cmd.linear.x = 0.0;
cmd.angular.z = 0.0;
commands_.push_back(cmd);
// 绘制五角星的五个顶点
for (int i = 0; i < 5; i++) {
// 前进
cmd.linear.x = 3.0;
cmd.angular.z = 0.0;
commands_.push_back(cmd);
// 转弯 144度 (形成五角星)
cmd.linear.x = 0.0;
cmd.angular.z = 2.513; // 144度 ≈ 2.513弧度
commands_.push_back(cmd);
}
}
void timer_callback()
{
if (current_command_ < commands_.size()) {
auto cmd = commands_[current_command_];
cmd_publisher_->publish(cmd);
RCLCPP_INFO(this->get_logger(), "执行绘图命令 %zu: 线速度=%.1f, 角速度=%.1f",
current_command_, cmd.linear.x, cmd.angular.z);
current_command_++;
} else {
// 绘制完成,停止
auto stop_cmd = geometry_msgs::msg::Twist();
cmd_publisher_->publish(stop_cmd);
if (step_counter_++ % 10 == 0) {
RCLCPP_INFO(this->get_logger(), "绘图完成! 海龟已停止运动");
}
}
}
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<TurtleDrawer>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
1.std::vector<geometry_msgs::msg::Twist> commands_; <vector>是cpp自带的容器,这里可以简单翻译为 我要创建一个commands_的 他的消息类型是geometry_msgs::msg::Twist 他是一个可以自动调节大小的数组容器。
然后在package.xml添加
<depend>rclcpp</depend> <depend>geometry_msgs</depend> <depend>turtlesim</depend>
这些依赖 可以看到rclcpp geometry_msgs turtlesim 都是我们代码中需要的,后面我们在自己的项目中就可以举一反三了。
最后是Cmakelists.txt
cmake_minimum_required(VERSION 3.8)
project(turtle_control_pkg)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(turtlesim REQUIRED)
# 编译控制器节点
add_executable(turtle_controller src/turtle_controller.cpp)
ament_target_dependencies(turtle_controller rclcpp geometry_msgs turtlesim)
# 编译监控节点
add_executable(pose_monitor src/pose_monitor.cpp)
ament_target_dependencies(pose_monitor rclcpp geometry_msgs turtlesim)
# 编译绘图节点
add_executable(turtle_drawer src/turtle_drawer.cpp)
ament_target_dependencies(turtle_drawer rclcpp geometry_msgs turtlesim)
# 安装节点
install(TARGETS
turtle_controller
pose_monitor
turtle_drawer
DESTINATION lib/${PROJECT_NAME}
)
# 安装启动文件(如果有)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
大家需要熟悉 add_executable() ament_target_dependencies() install() 这里面的写法
最后一步 运行编译
//先cd到工作文件夹
colcon build --packages-selet turtle_control_pkg
source install/setup.bash
ros2 run turtle_control_pkg turtle_controller
ros2 run turtle_control_pkg pose_monitor
ros2 run turtle_control_pkg turtle_drawer
然后观察你的小乌龟就可以啦,你也可以尝试修改代码,看看不同的效果。
2017

被折叠的 条评论
为什么被折叠?



