一个完整的机器人控制系统,包含自定义消息、服务和动作/行为的实现。

项目结构

robot_example/
├── msg/
│   ├── RobotStatus.msg
│   ├── SensorData.msg
│   └── BatteryInfo.msg
├── srv/
│   ├── SetMode.srv
│   └── GetRobotInfo.srv
├── action/
│   ├── NavigateToGoal.action
│   └── PickAndPlace.action
├── src/
│   ├── robot_controller.cpp (ROS1)
│   ├── robot_controller_ros2.cpp (ROS2)
│   ├── robot_client.cpp (ROS1)
│   └── robot_client_ros2.cpp (ROS2)
├── CMakeLists.txt
├── package.xml
└── README.md

消息定义

msg/RobotStatus.msg

# 机器人状态消息
string robot_id
int32 mode              # 0=idle, 1=moving, 2=working, 3=charging
geometry_msgs/Pose current_pose
BatteryInfo battery
SensorData sensors
float32 speed
bool is_emergency_stop
time last_update

msg/BatteryInfo.msg

# 电池信息
float32 voltage         # 电压 (V)
float32 current         # 电流 (A)
float32 percentage      # 电量百分比 (0-100)
float32 temperature     # 温度 (℃)
bool is_charging
int32 estimated_runtime # 预估运行时间 (秒)

msg/SensorData.msg

# 传感器数据
Header header
float32[] laser_ranges     # 激光雷达数据
geometry_msgs/Twist imu    # IMU数据
float32 temperature        # 环境温度
float32 humidity          # 湿度
bool[] obstacle_detected  # 障碍物检测 [前,后,左,右]

服务定义

srv/SetMode.srv

# 设置机器人模式
int32 mode              # 目标模式
string parameters       # 额外参数
---
bool success
string message
int32 previous_mode

srv/GetRobotInfo.srv

# 获取机器人信息
string robot_id
---
RobotStatus status
string[] available_modes
float32 uptime
string firmware_version
bool success

动作/行为定义

action/NavigateToGoal.action

# 导航到目标点
geometry_msgs/Pose target_pose
float32 max_speed
bool avoid_obstacles
---
bool success
geometry_msgs/Pose final_pose
float32 total_distance
float32 total_time
string result_message
---
geometry_msgs/Pose current_pose
float32 distance_remaining
float32 estimated_time_remaining
float32 current_speed
int32 progress_percentage

action/PickAndPlace.action

# 抓取和放置物体
string object_id
geometry_msgs/Pose pick_pose
geometry_msgs/Pose place_pose
float32 gripper_force
---
bool success
string result_message
geometry_msgs/Pose actual_pick_pose
geometry_msgs/Pose actual_place_pose
---
string current_phase    # "approaching", "picking", "lifting", "moving", "placing"
int32 progress_percentage
geometry_msgs/Pose current_pose

ROS1实现

编译配置 - CMakeLists.txt (ROS1)

cmake_minimum_required(VERSION 3.0.2)
project(robot_example)

# 查找依赖包
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  geometry_msgs
  sensor_msgs
  message_generation
  actionlib_msgs
  actionlib
)

# 添加消息文件
add_message_files(
  FILES
  RobotStatus.msg
  BatteryInfo.msg
  SensorData.msg
)

# 添加服务文件
add_service_files(
  FILES
  SetMode.srv
  GetRobotInfo.srv
)

# 添加动作文件
add_action_files(
  FILES
  NavigateToGoal.action
  PickAndPlace.action
)

# 生成消息
generate_messages(
  DEPENDENCIES
  std_msgs
  geometry_msgs
  sensor_msgs
  actionlib_msgs
)

# catkin包配置
catkin_package(
  CATKIN_DEPENDS
    message_runtime
    std_msgs
    geometry_msgs
    sensor_msgs
    actionlib_msgs
    actionlib
)

# 包含目录
include_directories(
  ${catkin_INCLUDE_DIRS}
)

# 构建可执行文件
add_executable(robot_controller src/robot_controller.cpp)
add_executable(robot_client src/robot_client.cpp)

# 链接库
target_link_libraries(robot_controller ${catkin_LIBRARIES})
target_link_libraries(robot_client ${catkin_LIBRARIES})

# 添加依赖
add_dependencies(robot_controller ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(robot_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

package.xml (ROS1)

<?xml version="1.0"?>
<package format="2">
  <name>robot_example</name>
  <version>1.0.0</version>
  <description>Complete ROS example with messages, services, and actions</description>
  
  <maintainer email="developer@example.com">Developer</maintainer>
  <license>MIT</license>

  <buildtool_depend>catkin</buildtool_depend>
  
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>geometry_msgs</build_depend>
  <build_depend>sensor_msgs</build_depend>
  <build_depend>message_generation</build_depend>
  <build_depend>actionlib_msgs</build_depend>
  <build_depend>actionlib</build_depend>

  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <exec_depend>geometry_msgs</exec_depend>
  <exec_depend>sensor_msgs</exec_depend>
  <exec_depend>message_runtime</exec_depend>
  <exec_depend>actionlib_msgs</exec_depend>
  <exec_depend>actionlib</exec_depend>
</package>

ROS1 机器人控制器实现

// src/robot_controller.cpp
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "robot_example/RobotStatus.h"
#include "robot_example/SetMode.h"
#include "robot_example/GetRobotInfo.h"
#include "robot_example/NavigateToGoalAction.h"
#include "robot_example/PickAndPlaceAction.h"

class RobotController {
private:
    ros::NodeHandle nh_;
    
    // 发布器
    ros::Publisher status_pub_;
    
    // 服务服务器
    ros::ServiceServer set_mode_srv_;
    ros::ServiceServer get_info_srv_;
    
    // 动作服务器
    actionlib::SimpleActionServer<robot_example::NavigateToGoalAction> nav_as_;
    actionlib::SimpleActionServer<robot_example::PickAndPlaceAction> pick_as_;
    
    // 机器人状态
    robot_example::RobotStatus robot_status_;
    int current_mode_;
    std::string robot_id_;

public:
    RobotController() : 
        nav_as_(nh_, "navigate_to_goal", 
                boost::bind(&RobotController::executeNavigation, this, _1), false),
        pick_as_(nh_, "pick_and_place", 
                 boost::bind(&RobotController::executePickAndPlace, this, _1), false) {
        
        robot_id_ = "robot_001";
        current_mode_ = 0; // idle
        
        // 初始化发布器
        status_pub_ = nh_.advertise<robot_example::RobotStatus>("robot_status", 10);
        
        // 初始化服务
        set_mode_srv_ = nh_.advertiseService("set_mode", 
            &RobotController::setModeCallback, this);
        get_info_srv_ = nh_.advertiseService("get_robot_info", 
            &RobotController::getRobotInfoCallback, this);
        
        // 启动动作服务器
        nav_as_.start();
        pick_as_.start();
        
        // 初始化机器人状态
        initializeRobotStatus();
        
        ROS_INFO("Robot Controller initialized with ID: %s", robot_id_.c_str());
    }
    
    void initializeRobotStatus() {
        robot_status_.robot_id = robot_id_;
        robot_status_.mode = current_mode_;
        robot_status_.current_pose.position.x = 0.0;
        robot_status_.current_pose.position.y = 0.0;
        robot_status_.current_pose.position.z = 0.0;
        robot_status_.speed = 0.0;
        robot_status_.is_emergency_stop = false;
        
        // 初始化电池信息
        robot_status_.battery.voltage = 24.0;
        robot_status_.battery.current = 2.0;
        robot_status_.battery.percentage = 85.0;
        robot_status_.battery.temperature = 25.0;
        robot_status_.battery.is_charging = false;
        robot_status_.battery.estimated_runtime = 3600;
        
        // 初始化传感器数据
        robot_status_.sensors.header.frame_id = "base_link";
        robot_status_.sensors.laser_ranges = {1.0, 1.5, 2.0, 1.2};
        robot_status_.sensors.temperature = 22.0;
        robot_status_.sensors.humidity = 45.0;
        robot_status_.sensors.obstacle_detected = {false, false, false, false};
    }
    
    bool setModeCallback(robot_example::SetMode::Request& req,
                        robot_example::SetMode::Response& res) {
        ROS_INFO("Setting robot mode to: %d", req.mode);
        
        if (req.mode >= 0 && req.mode <= 3) {
            res.previous_mode = current_mode_;
            current_mode_ = req.mode;
            robot_status_.mode = current_mode_;
            res.success = true;
            res.message = "Mode changed successfully";
        } else {
            res.success = false;
            res.message = "Invalid mode. Valid modes: 0-3";
        }
        
        return true;
    }
    
    bool getRobotInfoCallback(robot_example::GetRobotInfo::Request& req,
                             robot_example::GetRobotInfo::Response& res) {
        ROS_INFO("Robot info requested for ID: %s", req.robot_id.c_str());
        
        if (req.robot_id == robot_id_) {
            res.status = robot_status_;
            res.available_modes = {"idle", "moving", "working", "charging"};
            res.uptime = ros::Time::now().toSec();
            res.firmware_version = "1.2.3";
            res.success = true;
        } else {
            res.success = false;
        }
        
        return true;
    }
    
    void executeNavigation(const robot_example::NavigateToGoalGoalConstPtr& goal) {
        ROS_INFO("Starting navigation to target pose");
        
        ros::Rate rate(10);
        robot_example::NavigateToGoalFeedback feedback;
        robot_example::NavigateToGoalResult result;
        
        // 模拟导航过程
        double total_distance = 10.0;
        double start_time = ros::Time::now().toSec();
        
        for (int i = 0; i <= 100; ++i) {
            if (nav_as_.isPreemptRequested() || !ros::ok()) {
                ROS_INFO("Navigation preempted");
                nav_as_.setPreempted();
                return;
            }
            
            // 更新反馈
            feedback.distance_remaining = total_distance * (100 - i) / 100.0;
            feedback.estimated_time_remaining = feedback.distance_remaining / goal->max_speed;
            feedback.current_speed = goal->max_speed;
            feedback.progress_percentage = i;
            feedback.current_pose.position.x = i * 0.1;
            feedback.current_pose.position.y = i * 0.05;
            
            nav_as_.publishFeedback(feedback);
            
            // 更新机器人状态
            robot_status_.current_pose = feedback.current_pose;
            robot_status_.speed = feedback.current_speed;
            robot_status_.mode = 1; // moving
            
            rate.sleep();
        }
        
        // 设置结果
        result.success = true;
        result.final_pose = goal->target_pose;
        result.total_distance = total_distance;
        result.total_time = ros::Time::now().toSec() - start_time;
        result.result_message = "Navigation completed successfully";
        
        robot_status_.mode = 0; // idle
        robot_status_.speed = 0.0;
        
        ROS_INFO("Navigation completed successfully");
        nav_as_.setSucceeded(result);
    }
    
    void executePickAndPlace(const robot_example::PickAndPlaceGoalConstPtr& goal) {
        ROS_INFO("Starting pick and place operation for object: %s", goal->object_id.c_str());
        
        ros::Rate rate(10);
        robot_example::PickAndPlaceFeedback feedback;
        robot_example::PickAndPlaceResult result;
        
        std::vector<std::string> phases = {"approaching", "picking", "lifting", "moving", "placing"};
        
        for (size_t phase = 0; phase < phases.size(); ++phase) {
            for (int progress = 0; progress <= 100; progress += 10) {
                if (pick_as_.isPreemptRequested() || !ros::ok()) {
                    ROS_INFO("Pick and place preempted");
                    pick_as_.setPreempted();
                    return;
                }
                
                feedback.current_phase = phases[phase];
                feedback.progress_percentage = (phase * 100 + progress) / phases.size();
                
                // 根据阶段更新位置
                if (phase < 2) {
                    feedback.current_pose = goal->pick_pose;
                } else {
                    feedback.current_pose = goal->place_pose;
                }
                
                pick_as_.publishFeedback(feedback);
                robot_status_.mode = 2; // working
                
                rate.sleep();
            }
        }
        
        // 设置结果
        result.success = true;
        result.result_message = "Pick and place completed successfully";
        result.actual_pick_pose = goal->pick_pose;
        result.actual_place_pose = goal->place_pose;
        
        robot_status_.mode = 0; // idle
        
        ROS_INFO("Pick and place completed successfully");
        pick_as_.setSucceeded(result);
    }
    
    void publishStatus() {
        robot_status_.last_update = ros::Time::now();
        status_pub_.publish(robot_status_);
    }
    
    void spin() {
        ros::Rate rate(1); // 1Hz
        while (ros::ok()) {
            publishStatus();
            ros::spinOnce();
            rate.sleep();
        }
    }
};

int main(int argc, char** argv) {
    ros::init(argc, argv, "robot_controller");
    
    RobotController robot;
    robot.spin();
    
    return 0;
}

ROS1 客户端实现

// src/robot_client.cpp
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include "robot_example/RobotStatus.h"
#include "robot_example/SetMode.h"
#include "robot_example/GetRobotInfo.h"
#include "robot_example/NavigateToGoalAction.h"

typedef actionlib::SimpleActionClient<robot_example::NavigateToGoalAction> NavigationClient;

class RobotClient {
private:
    ros::NodeHandle nh_;
    ros::Subscriber status_sub_;
    ros::ServiceClient set_mode_client_;
    ros::ServiceClient get_info_client_;
    std::unique_ptr<NavigationClient> nav_client_;

public:
    RobotClient() {
        // 初始化订阅器
        status_sub_ = nh_.subscribe("robot_status", 10, 
            &RobotClient::statusCallback, this);
        
        // 初始化服务客户端
        set_mode_client_ = nh_.serviceClient<robot_example::SetMode>("set_mode");
        get_info_client_ = nh_.serviceClient<robot_example::GetRobotInfo>("get_robot_info");
        
        // 初始化动作客户端
        nav_client_.reset(new NavigationClient("navigate_to_goal", true));
        
        ROS_INFO("Robot Client initialized");
    }
    
    void statusCallback(const robot_example::RobotStatus::ConstPtr& msg) {
        ROS_INFO_THROTTLE(5, "Robot %s - Mode: %d, Speed: %.2f, Battery: %.1f%%", 
                         msg->robot_id.c_str(), msg->mode, msg->speed, 
                         msg->battery.percentage);
    }
    
    bool setRobotMode(int mode) {
        robot_example::SetMode srv;
        srv.request.mode = mode;
        srv.request.parameters = "";
        
        if (set_mode_client_.call(srv)) {
            ROS_INFO("Mode change result: %s", srv.response.message.c_str());
            return srv.response.success;
        } else {
            ROS_ERROR("Failed to call set_mode service");
            return false;
        }
    }
    
    void getRobotInfo() {
        robot_example::GetRobotInfo srv;
        srv.request.robot_id = "robot_001";
        
        if (get_info_client_.call(srv)) {
            if (srv.response.success) {
                ROS_INFO("Robot Info - Firmware: %s, Uptime: %.1f", 
                         srv.response.firmware_version.c_str(), 
                         srv.response.uptime);
            } else {
                ROS_WARN("Robot not found");
            }
        } else {
            ROS_ERROR("Failed to call get_robot_info service");
        }
    }
    
    void navigateToGoal(double x, double y) {
        ROS_INFO("Waiting for navigation server...");
        nav_client_->waitForServer();
        
        robot_example::NavigateToGoalGoal goal;
        goal.target_pose.position.x = x;
        goal.target_pose.position.y = y;
        goal.target_pose.position.z = 0.0;
        goal.max_speed = 1.0;
        goal.avoid_obstacles = true;
        
        ROS_INFO("Sending navigation goal to (%.2f, %.2f)", x, y);
        nav_client_->sendGoal(goal,
            boost::bind(&RobotClient::navigationDone, this, _1, _2),
            NavigationClient::SimpleActiveCallback(),
            boost::bind(&RobotClient::navigationFeedback, this, _1));
    }
    
    void navigationDone(const actionlib::SimpleClientGoalState& state,
                       const robot_example::NavigateToGoalResultConstPtr& result) {
        ROS_INFO("Navigation finished: %s", state.toString().c_str());
        if (result->success) {
            ROS_INFO("Distance: %.2f, Time: %.2f", 
                     result->total_distance, result->total_time);
        }
    }
    
    void navigationFeedback(const robot_example::NavigateToGoalFeedbackConstPtr& feedback) {
        ROS_INFO_THROTTLE(2, "Navigation progress: %d%%, Distance remaining: %.2f", 
                         feedback->progress_percentage, feedback->distance_remaining);
    }
    
    void testAllFeatures() {
        ros::Duration(2.0).sleep(); // 等待连接建立
        
        // 测试服务
        getRobotInfo();
        setRobotMode(1); // 设置为moving模式
        
        // 测试动作
        navigateToGoal(5.0, 3.0);
        
        // 等待动作完成
        nav_client_->waitForResult();
        
        setRobotMode(0); // 设置回idle模式
    }
};

int main(int argc, char** argv) {
    ros::init(argc, argv, "robot_client");
    
    RobotClient client;
    client.testAllFeatures();
    
    ros::spin();
    return 0;
}

ROS2实现

编译配置 - CMakeLists.txt (ROS2)

cmake_minimum_required(VERSION 3.8)
project(robot_example)

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(rclcpp_action REQUIRED)
find_package(std_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(action_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)

# 生成接口文件
rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/RobotStatus.msg"
  "msg/BatteryInfo.msg"
  "msg/SensorData.msg"
  "srv/SetMode.srv"
  "srv/GetRobotInfo.srv"
  "action/NavigateToGoal.action"
  "action/PickAndPlace.action"
  DEPENDENCIES std_msgs geometry_msgs sensor_msgs action_msgs
)

# 构建可执行文件
add_executable(robot_controller_ros2 src/robot_controller_ros2.cpp)
add_executable(robot_client_ros2 src/robot_client_ros2.cpp)

ament_target_dependencies(robot_controller_ros2 
  rclcpp rclcpp_action std_msgs geometry_msgs sensor_msgs)
ament_target_dependencies(robot_client_ros2 
  rclcpp rclcpp_action std_msgs geometry_msgs sensor_msgs)

# 链接生成的接口
rosidl_target_interfaces(robot_controller_ros2 ${PROJECT_NAME} "rosidl_typesupport_cpp")
rosidl_target_interfaces(robot_client_ros2 ${PROJECT_NAME} "rosidl_typesupport_cpp")

# 安装
install(TARGETS
  robot_controller_ros2
  robot_client_ros2
  DESTINATION lib/${PROJECT_NAME}
)

# 安装其他文件
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}/)

ament_package()

package.xml (ROS2)

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>robot_example</name>
  <version>1.0.0</version>
  <description>Complete ROS2 example with messages, services, and actions</description>
  <maintainer email="developer@example.com">Developer</maintainer>
  <license>MIT</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <buildtool_depend>rosidl_default_generators</buildtool_depend>

  <depend>rclcpp</depend>
  <depend>rclcpp_action</depend>
  <depend>std_msgs</depend>
  <depend>geometry_msgs</depend>
  <depend>sensor_msgs</depend>
  <depend>action_msgs</depend>

  <exec_depend>rosidl_default_runtime</exec_depend>

  <member_of_group>rosidl_interface_packages</member_of_group>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

ROS2 机器人控制器实现

// src/robot_controller_ros2.cpp
#include <functional>
#include <memory>
#include <thread>
#include <chrono>

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "robot_example/msg/robot_status.hpp"
#include "robot_example/srv/set_mode.hpp"
#include "robot_example/srv/get_robot_info.hpp"
#include "robot_example/action/navigate_to_goal.hpp"
#include "robot_example/action/pick_and_place.hpp"

using namespace std::chrono_literals;

class RobotController : public rclcpp::Node {
public:
    using NavigateToGoal = robot_example::action::NavigateToGoal;
    using PickAndPlace = robot_example::action::PickAndPlace;
    using GoalHandleNav = rclcpp_action::ServerGoalHandle<NavigateToGoal>;
    using GoalHandlePick = rclcpp_action::ServerGoalHandle<PickAndPlace>;

    RobotController() : Node("robot_controller"), robot_id_("robot_001"), current_mode_(0) {
        // 发布器
        status_pub_ = this->create_publisher<robot_example::msg::RobotStatus>(
            "robot_status", 10);

        // 服务服务器
        set_mode_srv_ = this->create_service<robot_example::srv::SetMode>(
            "set_mode", std::bind(&RobotController::setModeCallback, this,
            std::placeholders::_1, std::placeholders::_2));
        
        get_info_srv_ = this->create_service<robot_example::srv::GetRobotInfo>(
            "get_robot_info", std::bind(&RobotController::getRobotInfoCallback, this,
            std::placeholders::_1, std::placeholders::_2));

        // 动作服务器
        nav_action_server_ = rclcpp_action::create_server<NavigateToGoal>(
            this, "navigate_to_goal",
            std::bind(&RobotController::handleNavGoal, this, std::placeholders::_1, std::placeholders::_2),
            std::bind(&RobotController::handleNavCancel, this, std::placeholders::_1),
            std::bind(&RobotController::handleNavAccepted, this, std::placeholders::_1));

        pick_action_server_ = rclcpp_action::create_server<PickAndPlace>(
            this, "pick_and_place",
            std::bind(&RobotController::handlePickGoal, this, std::placeholders::_1, std::placeholders::_2),
            std::bind(&RobotController::handlePickCancel, this, std::placeholders::_1),
            std::bind(&RobotController::handlePickAccepted, this, std::placeholders::_1));

        // 状态发布定时器
        timer_ = this->create_wall_timer(1s, std::bind(&RobotController::publishStatus, this));

        initializeRobotStatus();
        RCLCPP_INFO(this->get_logger(), "Robot Controller initialized with ID: %s", robot_id_.c_str());
    }

private:
    // 成员变量
    std::string robot_id_;
    int32_t current_mode_;
    robot_example::msg::RobotStatus robot_status_;

    // ROS2组件
    rclcpp::Publisher<robot_example::msg::RobotStatus>::SharedPtr status_pub_;
    rclcpp::Service<robot_example::srv::SetMode>::SharedPtr set_mode_srv_;
    rclcpp::Service<robot_example::srv::GetRobotInfo>::SharedPtr get_info_srv_;
    rclcpp_action::Server<NavigateToGoal>::SharedPtr nav_action_server_;
    rclcpp_action::Server<PickAndPlace>::SharedPtr pick_action_server_;
    rclcpp::TimerBase::SharedPtr timer_;

    void initializeRobotStatus() {
        robot_status_.robot_id = robot_id_;
        robot_status_.mode = current_mode_;
        robot_status_.current_pose.position.x = 0.0;
        robot_status_.current_pose.position.y = 0.0;
        robot_status_.current_pose.position.z = 0.0;
        robot_status_.speed = 0.0;
        robot_status_.is_emergency_stop = false;

        // 初始化电池信息
        robot_status_.battery.voltage = 24.0;
        robot_status_.battery.current = 2.0;
        robot_status_.battery.percentage = 85.0;
        robot_status_.battery.temperature = 25.0;
        robot_status_.battery.is_charging = false;
        robot_status_.battery.estimated_runtime = 3600;

        // 初始化传感器数据
        robot_status_.sensors.header.frame_id = "base_link";
        robot_status_.sensors.laser_ranges = {1.0, 1.5, 2.0, 1.2};
        robot_status_.sensors.temperature = 22.0;
        robot_status_.sensors.humidity = 45.0;
        robot_status_.sensors.obstacle_detected = {false, false, false, false};
    }

    void setModeCallback(
        const std::shared_ptr<robot_example::srv::SetMode::Request> request,
        std::shared_ptr<robot_example::srv::SetMode::Response> response) {
        
        RCLCPP_INFO(this->get_logger(), "Setting robot mode to: %d", request->mode);
        
        if (request->mode >= 0 && request->mode <= 3) {
            response->previous_mode = current_mode_;
            current_mode_ = request->mode;
            robot_status_.mode = current_mode_;
            response->success = true;
            response->message = "Mode changed successfully";
        } else {
            response->success = false;
            response->message = "Invalid mode. Valid modes: 0-3";
        }
    }

    void getRobotInfoCallback(
        const std::shared_ptr<robot_example::srv::GetRobotInfo::Request> request,
        std::shared_ptr<robot_example::srv::GetRobotInfo::Response> response) {
        
        RCLCPP_INFO(this->get_logger(), "Robot info requested for ID: %s", request->robot_id.c_str());
        
        if (request->robot_id == robot_id_) {
            response->status = robot_status_;
            response->available_modes = {"idle", "moving", "working", "charging"};
            response->uptime = this->get_clock()->now().seconds();
            response->firmware_version = "1.2.3";
            response->success = true;
        } else {
            response->success = false;
        }
    }

    // 导航动作处理
    rclcpp_action::GoalResponse handleNavGoal(
        const rclcpp_action::GoalUUID & uuid,
        std::shared_ptr<const NavigateToGoal::Goal> goal) {
        
        RCLCPP_INFO(this->get_logger(), "Received navigation goal request");
        (void)uuid;
        return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
    }

    rclcpp_action::CancelResponse handleNavCancel(
        const std::shared_ptr<GoalHandleNav> goal_handle) {
        
        RCLCPP_INFO(this->get_logger(), "Received request to cancel navigation");
        (void)goal_handle;
        return rclcpp_action::CancelResponse::ACCEPT;
    }

    void handleNavAccepted(const std::shared_ptr<GoalHandleNav> goal_handle) {
        // 在新线程中执行导航
        std::thread{std::bind(&RobotController::executeNavigation, this, goal_handle)}.detach();
    }

    void executeNavigation(const std::shared_ptr<GoalHandleNav> goal_handle) {
        RCLCPP_INFO(this->get_logger(), "Starting navigation execution");
        
        const auto goal = goal_handle->get_goal();
        auto feedback = std::make_shared<NavigateToGoal::Feedback>();
        auto result = std::make_shared<NavigateToGoal::Result>();
        
        // 模拟导航过程
        double total_distance = 10.0;
        auto start_time = this->get_clock()->now();
        
        for (int i = 0; i <= 100; ++i) {
            if (goal_handle->is_canceling()) {
                result->success = false;
                result->result_message = "Navigation was cancelled";
                goal_handle->canceled(result);
                RCLCPP_INFO(this->get_logger(), "Navigation cancelled");
                return;
            }
            
            // 更新反馈
            feedback->distance_remaining = total_distance * (100 - i) / 100.0;
            feedback->estimated_time_remaining = feedback->distance_remaining / goal->max_speed;
            feedback->current_speed = goal->max_speed;
            feedback->progress_percentage = i;
            feedback->current_pose.position.x = i * 0.1;
            feedback->current_pose.position.y = i * 0.05;
            
            goal_handle->publish_feedback(feedback);
            
            // 更新机器人状态
            robot_status_.current_pose = feedback->current_pose;
            robot_status_.speed = feedback->current_speed;
            robot_status_.mode = 1; // moving
            
            std::this_thread::sleep_for(100ms);
        }
        
        // 设置结果
        result->success = true;
        result->final_pose = goal->target_pose;
        result->total_distance = total_distance;
        result->total_time = (this->get_clock()->now() - start_time).seconds();
        result->result_message = "Navigation completed successfully";
        
        robot_status_.mode = 0; // idle
        robot_status_.speed = 0.0;
        
        goal_handle->succeed(result);
        RCLCPP_INFO(this->get_logger(), "Navigation completed successfully");
    }

    // 抓取放置动作处理
    rclcpp_action::GoalResponse handlePickGoal(
        const rclcpp_action::GoalUUID & uuid,
        std::shared_ptr<const PickAndPlace::Goal> goal) {
        
        RCLCPP_INFO(this->get_logger(), "Received pick and place goal for object: %s", 
                   goal->object_id.c_str());
        (void)uuid;
        return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
    }

    rclcpp_action::CancelResponse handlePickCancel(
        const std::shared_ptr<GoalHandlePick> goal_handle) {
        
        RCLCPP_INFO(this->get_logger(), "Received request to cancel pick and place");
        (void)goal_handle;
        return rclcpp_action::CancelResponse::ACCEPT;
    }

    void handlePickAccepted(const std::shared_ptr<GoalHandlePick> goal_handle) {
        // 在新线程中执行抓取放置
        std::thread{std::bind(&RobotController::executePickAndPlace, this, goal_handle)}.detach();
    }

    void executePickAndPlace(const std::shared_ptr<GoalHandlePick> goal_handle) {
        RCLCPP_INFO(this->get_logger(), "Starting pick and place execution");
        
        const auto goal = goal_handle->get_goal();
        auto feedback = std::make_shared<PickAndPlace::Feedback>();
        auto result = std::make_shared<PickAndPlace::Result>();
        
        std::vector<std::string> phases = {"approaching", "picking", "lifting", "moving", "placing"};
        
        for (size_t phase = 0; phase < phases.size(); ++phase) {
            for (int progress = 0; progress <= 100; progress += 10) {
                if (goal_handle->is_canceling()) {
                    result->success = false;
                    result->result_message = "Pick and place was cancelled";
                    goal_handle->canceled(result);
                    RCLCPP_INFO(this->get_logger(), "Pick and place cancelled");
                    return;
                }
                
                feedback->current_phase = phases[phase];
                feedback->progress_percentage = (phase * 100 + progress) / phases.size();
                
                // 根据阶段更新位置
                if (phase < 2) {
                    feedback->current_pose = goal->pick_pose;
                } else {
                    feedback->current_pose = goal->place_pose;
                }
                
                goal_handle->publish_feedback(feedback);
                robot_status_.mode = 2; // working
                
                std::this_thread::sleep_for(100ms);
            }
        }
        
        // 设置结果
        result->success = true;
        result->result_message = "Pick and place completed successfully";
        result->actual_pick_pose = goal->pick_pose;
        result->actual_place_pose = goal->place_pose;
        
        robot_status_.mode = 0; // idle
        
        goal_handle->succeed(result);
        RCLCPP_INFO(this->get_logger(), "Pick and place completed successfully");
    }

    void publishStatus() {
        robot_status_.last_update = this->get_clock()->now();
        status_pub_->publish(robot_status_);
    }
};

int main(int argc, char * argv[]) {
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<RobotController>());
    rclcpp::shutdown();
    return 0;
}

ROS2 机器人客户端实现

// src/robot_client_ros2.cpp - 完整版本
#include <functional>
#include <memory>
#include <chrono>
#include <future>

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "robot_example/msg/robot_status.hpp"
#include "robot_example/srv/set_mode.hpp"
#include "robot_example/srv/get_robot_info.hpp"
#include "robot_example/action/navigate_to_goal.hpp"
#include "robot_example/action/pick_and_place.hpp"

using namespace std::chrono_literals;

class RobotClient : public rclcpp::Node {
public:
    using NavigateToGoal = robot_example::action::NavigateToGoal;
    using PickAndPlace = robot_example::action::PickAndPlace;
    using GoalHandleNav = rclcpp_action::ClientGoalHandle<NavigateToGoal>;
    using GoalHandlePick = rclcpp_action::ClientGoalHandle<PickAndPlace>;

    RobotClient() : Node("robot_client") {
        // 初始化订阅器
        status_sub_ = this->create_subscription<robot_example::msg::RobotStatus>(
            "robot_status", 10, std::bind(&RobotClient::statusCallback, this, std::placeholders::_1));

        // 初始化服务客户端
        set_mode_client_ = this->create_client<robot_example::srv::SetMode>("set_mode");
        get_info_client_ = this->create_client<robot_example::srv::GetRobotInfo>("get_robot_info");

        // 初始化动作客户端
        nav_action_client_ = rclcpp_action::create_client<NavigateToGoal>(this, "navigate_to_goal");
        pick_action_client_ = rclcpp_action::create_client<PickAndPlace>(this, "pick_and_place");

        RCLCPP_INFO(this->get_logger(), "Robot Client initialized");
    }

    void statusCallback(const robot_example::msg::RobotStatus::SharedPtr msg) {
        static auto last_log_time = this->get_clock()->now();
        auto current_time = this->get_clock()->now();
        
        // 每5秒打印一次状态信息
        if ((current_time - last_log_time).seconds() >= 5.0) {
            RCLCPP_INFO(this->get_logger(), 
                       "Robot %s - Mode: %d, Speed: %.2f, Battery: %.1f%%",
                       msg->robot_id.c_str(), msg->mode, msg->speed, 
                       msg->battery.percentage);
            last_log_time = current_time;
        }
    }

    bool setRobotMode(int32_t mode) {
        // 等待服务可用
        while (!set_mode_client_->wait_for_service(1s)) {
            if (!rclcpp::ok()) {
                RCLCPP_ERROR(this->get_logger(), "Client interrupted while waiting for service");
                return false;
            }
            RCLCPP_INFO(this->get_logger(), "Waiting for set_mode service...");
        }

        auto request = std::make_shared<robot_example::srv::SetMode::Request>();
        request->mode = mode;
        request->parameters = "";

        auto result_future = set_mode_client_->async_send_request(request);
        
        // 等待结果
        if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), result_future) ==
            rclcpp::FutureReturnCode::SUCCESS) {
            auto result = result_future.get();
            RCLCPP_INFO(this->get_logger(), "Mode change result: %s", result->message.c_str());
            return result->success;
        } else {
            RCLCPP_ERROR(this->get_logger(), "Failed to call set_mode service");
            return false;
        }
    }

    void getRobotInfo() {
        // 等待服务可用
        while (!get_info_client_->wait_for_service(1s)) {
            if (!rclcpp::ok()) {
                RCLCPP_ERROR(this->get_logger(), "Client interrupted while waiting for service");
                return;
            }
            RCLCPP_INFO(this->get_logger(), "Waiting for get_robot_info service...");
        }

        auto request = std::make_shared<robot_example::srv::GetRobotInfo::Request>();
        request->robot_id = "robot_001";

        auto result_future = get_info_client_->async_send_request(request);
        
        // 等待结果
        if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), result_future) ==
            rclcpp::FutureReturnCode::SUCCESS) {
            auto result = result_future.get();
            if (result->success) {
                RCLCPP_INFO(this->get_logger(), 
                           "Robot Info - Firmware: %s, Uptime: %.1f",
                           result->firmware_version.c_str(), result->uptime);
            } else {
                RCLCPP_WARN(this->get_logger(), "Robot not found");
            }
        } else {
            RCLCPP_ERROR(this->get_logger(), "Failed to call get_robot_info service");
        }
    }

    void navigateToGoal(double x, double y) {
        // 等待动作服务器
        if (!nav_action_client_->wait_for_action_server(10s)) {
            RCLCPP_ERROR(this->get_logger(), "Navigation action server not available");
            return;
        }

        auto goal_msg = NavigateToGoal::Goal();
        goal_msg.target_pose.position.x = x;
        goal_msg.target_pose.position.y = y;
        goal_msg.target_pose.position.z = 0.0;
        goal_msg.max_speed = 1.0;
        goal_msg.avoid_obstacles = true;

        RCLCPP_INFO(this->get_logger(), "Sending navigation goal to (%.2f, %.2f)", x, y);

        auto send_goal_options = rclcpp_action::Client<NavigateToGoal>::SendGoalOptions();
        send_goal_options.goal_response_callback = 
            std::bind(&RobotClient::navGoalResponseCallback, this, std::placeholders::_1);
        send_goal_options.feedback_callback = 
            std::bind(&RobotClient::navFeedbackCallback, this, std::placeholders::_1, std::placeholders::_2);
        send_goal_options.result_callback = 
            std::bind(&RobotClient::navResultCallback, this, std::placeholders::_1);

        nav_action_client_->async_send_goal(goal_msg, send_goal_options);
    }

    void navGoalResponseCallback(std::shared_future<GoalHandleNav::SharedPtr> future) {
        auto goal_handle = future.get();
        if (!goal_handle) {
            RCLCPP_ERROR(this->get_logger(), "Navigation goal was rejected by server");
        } else {
            RCLCPP_INFO(this->get_logger(), "Navigation goal accepted by server, waiting for result");
        }
    }

    void navFeedbackCallback(
        GoalHandleNav::SharedPtr,
        const std::shared_ptr<const NavigateToGoal::Feedback> feedback) {
        
        static auto last_feedback_time = this->get_clock()->now();
        auto current_time = this->get_clock()->now();
        
        // 每2秒打印一次反馈信息
        if ((current_time - last_feedback_time).seconds() >= 2.0) {
            RCLCPP_INFO(this->get_logger(), 
                       "Navigation progress: %d%%, Distance remaining: %.2f",
                       feedback->progress_percentage, feedback->distance_remaining);
            last_feedback_time = current_time;
        }
    }

    void navResultCallback(const GoalHandleNav::WrappedResult & result) {
        switch (result.code) {
            case rclcpp_action::ResultCode::SUCCEEDED:
                RCLCPP_INFO(this->get_logger(), "Navigation succeeded!");
                if (result.result->success) {
                    RCLCPP_INFO(this->get_logger(), 
                               "Distance: %.2f, Time: %.2f",
                               result.result->total_distance, result.result->total_time);
                }
                break;
            case rclcpp_action::ResultCode::ABORTED:
                RCLCPP_ERROR(this->get_logger(), "Navigation was aborted");
                break;
            case rclcpp_action::ResultCode::CANCELED:
                RCLCPP_ERROR(this->get_logger(), "Navigation was canceled");
                break;
            default:
                RCLCPP_ERROR(this->get_logger(), "Unknown navigation result code");
                break;
        }
    }

    void pickAndPlace(const std::string& object_id, double pick_x, double pick_y, 
                     double place_x, double place_y) {
        // 等待动作服务器
        if (!pick_action_client_->wait_for_action_server(10s)) {
            RCLCPP_ERROR(this->get_logger(), "Pick and place action server not available");
            return;
        }

        auto goal_msg = PickAndPlace::Goal();
        goal_msg.object_id = object_id;
        goal_msg.pick_pose.position.x = pick_x;
        goal_msg.pick_pose.position.y = pick_y;
        goal_msg.pick_pose.position.z = 0.0;
        goal_msg.place_pose.position.x = place_x;
        goal_msg.place_pose.position.y = place_y;
        goal_msg.place_pose.position.z = 0.0;
        goal_msg.gripper_force = 10.0;

        RCLCPP_INFO(this->get_logger(), "Sending pick and place goal for object: %s", object_id.c_str());

        auto send_goal_options = rclcpp_action::Client<PickAndPlace>::SendGoalOptions();
        send_goal_options.goal_response_callback = 
            std::bind(&RobotClient::pickGoalResponseCallback, this, std::placeholders::_1);
        send_goal_options.feedback_callback = 
            std::bind(&RobotClient::pickFeedbackCallback, this, std::placeholders::_1, std::placeholders::_2);
        send_goal_options.result_callback = 
            std::bind(&RobotClient::pickResultCallback, this, std::placeholders::_1);

        pick_action_client_->async_send_goal(goal_msg, send_goal_options);
    }

    void pickGoalResponseCallback(std::shared_future<GoalHandlePick::SharedPtr> future) {
        auto goal_handle = future.get();
        if (!goal_handle) {
            RCLCPP_ERROR(this->get_logger(), "Pick and place goal was rejected by server");
        } else {
            RCLCPP_INFO(this->get_logger(), "Pick and place goal accepted by server, waiting for result");
        }
    }

    void pickFeedbackCallback(
        GoalHandlePick::SharedPtr,
        const std::shared_ptr<const PickAndPlace::Feedback> feedback) {
        
        static auto last_feedback_time = this->get_clock()->now();
        auto current_time = this->get_clock()->now();
        
        // 每2秒打印一次反馈信息
        if ((current_time - last_feedback_time).seconds() >= 2.0) {
            RCLCPP_INFO(this->get_logger(), 
                       "Pick and place progress: %s - %d%%",
                       feedback->current_phase.c_str(), feedback->progress_percentage);
            last_feedback_time = current_time;
        }
    }

    void pickResultCallback(const GoalHandlePick::WrappedResult & result) {
        switch (result.code) {
            case rclcpp_action::ResultCode::SUCCEEDED:
                RCLCPP_INFO(this->get_logger(), "Pick and place succeeded!");
                RCLCPP_INFO(this->get_logger(), "Result: %s", result.result->result_message.c_str());
                break;
            case rclcpp_action::ResultCode::ABORTED:
                RCLCPP_ERROR(this->get_logger(), "Pick and place was aborted");
                break;
            case rclcpp_action::ResultCode::CANCELED:
                RCLCPP_ERROR(this->get_logger(), "Pick and place was canceled");
                break;
            default:
                RCLCPP_ERROR(this->get_logger(), "Unknown pick and place result code");
                break;
        }
    }

    void testAllFeatures() {
        // 等待一段时间确保连接建立
        rclcpp::sleep_for(2s);

        // 测试服务
        RCLCPP_INFO(this->get_logger(), "Testing robot services...");
        getRobotInfo();
        setRobotMode(1); // 设置为moving模式

        // 测试导航动作
        RCLCPP_INFO(this->get_logger(), "Testing navigation action...");
        navigateToGoal(5.0, 3.0);
        
        // 等待导航完成
        rclcpp::sleep_for(15s);

        // 测试抓取放置动作
        RCLCPP_INFO(this->get_logger(), "Testing pick and place action...");
        pickAndPlace("cube_001", 2.0, 1.0, 4.0, 2.0);
        
        // 等待抓取放置完成
        rclcpp::sleep_for(15s);

        setRobotMode(0); // 设置回idle模式
        RCLCPP_INFO(this->get_logger(), "All tests completed!");
    }

private:
    // 订阅器和客户端
    rclcpp::Subscription<robot_example::msg::RobotStatus>::SharedPtr status_sub_;
    rclcpp::Client<robot_example::srv::SetMode>::SharedPtr set_mode_client_;
    rclcpp::Client<robot_example::srv::GetRobotInfo>::SharedPtr get_info_client_;
    rclcpp_action::Client<NavigateToGoal>::SharedPtr nav_action_client_;
    rclcpp_action::Client<PickAndPlace>::SharedPtr pick_action_client_;
};

int main(int argc, char * argv[]) {
    rclcpp::init(argc, argv);
    
    auto client = std::make_shared<RobotClient>();
    
    // 启动测试
    std::thread test_thread([&client]() {
        client->testAllFeatures();
    });
    
    rclcpp::spin(client);
    
    if (test_thread.joinable()) {
        test_thread.join();
    }
    
    rclcpp::shutdown();
    return 0;
}