ROS2 最基础使用案例

网上关于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

然后观察你的小乌龟就可以啦,你也可以尝试修改代码,看看不同的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值