开机怎么打开以前的写好的ROS程序
source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash
ros2 launch arm_moveit_config demo.launch.py
cd ~/ros2_ws
colcon build --packages-select arm_motion_demo
source ~/ros2_ws/install/setup.bash
ros2 run arm_motion_demo collision_check
#机械臂仿真流程
URDF建模->Rviz可视化->Moveit运动规划->C++程序调用Moveit
->加入正逆运动控制算法->连接真机测试
- 建立一个 ROS 2 节点——让程序进入 ROS 系统。
- 获取机器人模型、关节状态和环境——知道要检查什么。
- 调用 MoveIt 的碰撞检查。
- 输出结果——有没有碰撞,必要时报告碰撞部位。
第一步:创建自己的 ROS 包
在Ubuntu 的新终端中执行,不是在 Windows 中执行:
source /opt/ros/humble/setup.bash source ~/ros2_ws/install/setup.bash cd ~/ros2_ws/src接着创建包。如果已经存在arm_motion_demo,就不要重复创建,告诉我即可。
ros2 pkg create arm_motion_demo \ --build-type ament_cmake \ --dependencies rclcpp moveit_ros_planning moveit_msgs| 内容 | 作用 |
|---|---|
arm_motion_demo | 我们的程序包名称 |
ament_cmake | 用 CMake 管理、编译这个 C++ 包 |
rclcpp | 编写 ROS 2 C++ 节点所需的库 |
moveit_ros_planning | 后面加载模型、使用规划场景等功能 |
moveit_msgs | 与 MoveIt 通信所用的消息和服务类型 |
auto scene_client = ...这行代码相当于让节点执行某个操作,具体来说就是调用create_client函数,并把返回结果赋值给scene_client这个变量。
🎯node->和request->的本质区别
node->create_client(...):node是节点对象。node->意味着“让节点执行创建客户端这个操作”。这是让对象“干活”。request->components...:request是一个数据包(请求对象)。request->意味着“访问这个数据包里面的内容”。这是给这个数据包“填字”。
C++库函数
std::makeshared<...>()
创建一个对象,并把他放到一个智能指针里
<...>填写模板参数:告诉它要造什么类型的对象
()填写构造函数参数:告诉它造这个对象的时候,传什么参数
spin_until_future_complete
作用:让 ROS 处理这个节点的通信事件,直到收到回答、等待超时或被中断。
使用:node future std::chrono::seconds(5)
RCLCPP_INFO(
node->get_logger(), // 参数1:谁在说话
"Received %zu joint names and %zu positions.", // 参数2:要印的模板
joint_state.name.size(), // 参数3:替换第一个 %zu
joint_state.position.size()); // 参数4:替换第二个 %zu
作用:终端打印输出日志。
C++语法
对象是什么?
(1)C++里,一个名字不是简单的变量,而是一个"对象",对象由数据和函数构成。
(2)一个简单的对象,天生内部就有调用某些函数的能力,但是函数(属于类)并不是存储在这个对象里的。
🔍 例:create_client返回的是什么?
node->create_client<...>(...)调用后,ROS 2 内部会做这些事:
分配一块内存。
在内存里创建一个
rclcpp::Client类型的对象。在这个对象里,把
wait_for_service、async_send_request等函数“绑定”上去。返回一个智能指针,指向这个对象。
你把这个指针存进了
scene_client。
所以scene_client指向的不是一个“空壳”,而是一个“功能齐全的客户端对象”。
对象和客户端有啥区别?
(1)客户端是负责向服务端发送请求,接收回答的。
(2)ROS里面有很多功能包,编程时使用这些包提供的服务,需要先向服务端发送请求(程序是请求端)创建客户端是为了完成这个服务的通信。
(3)对象是实体,客户端是角色。客户端相当于是一个封装好的特殊对象,里面也是由数据构成的。
&是干啥用的?
auto response = future.get(); const auto &joint_state = response->scene.robot_state.joint_state;相当于是从future.get()函数里提取出数据以后,存入了response,又定义 了一个只读的const auto对象,将从response里面的scene.robot里面的joint_state读到的结果贴上一个标签。
示例代码:
获取机器人关节角度,检测当前状态是否干涉,创建了两个客户端,一个获取机器人关节状态,一个读取客户端一的数据,向Moveit发送服务请求,进行干涉检查。
#include <rclcpp/rclcpp.hpp> #include <moveit_msgs/srv/get_planning_scene.hpp> #include <moveit_msgs/msg/planning_scene_components.hpp> #include <moveit_msgs/srv/get_state_validity.hpp> #include <chrono> #include <cstddef> //rclcpp是C++核心库 int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = rclcpp::Node::make_shared("arm_collision_check"); RCLCPP_INFO(node->get_logger(), "Collision checker started."); //创建服务客户端:(完成获取机器人状态等场景数据) //ROS2向Moveit2请求规划场景 auto sence_client = node->create_client<moveit_msgs::srv::GetPlanningScene>( "/get_planning_scene"); //“让 node 这个节点,去创建一个专门连接 /get_planning_scene 服务的客户端,服务类型是 GetPlanningScene。” //node->的意思是:让node这个节点执行某个操作 //auto:自动识别右边是什么类型,并赋予这个类型 if (!sence_client->wait_for_service(std::chrono::seconds(5))) { RCLCPP_ERROR(node->get_logger(), "Planning scene service not available."); rclcpp::shutdown(); return 1; } RCLCPP_INFO(node->get_logger(), "Planning scene service is available."); //向Moveit请求当前关节状态 auto request = std::make_shared<moveit_msgs::srv::GetPlanningScene::Request>(); request->components.components = moveit_msgs::msg::PlanningSceneComponents::ROBOT_STATE; //发送请求: auto future = sence_client->async_send_request(request); //等待处理并回答: auto status = rclcpp::spin_until_future_complete( node,future,std::chrono::seconds(5) ); if(status != rclcpp::FutureReturnCode::SUCCESS) { RCLCPP_ERROR(node->get_logger(),"failed to receive planning scene"); rclcpp::shutdown(); return 1; } auto response = future.get(); const auto &joint_state = response->scene.robot_state.joint_state; RCLCPP_INFO( node->get_logger(), "Receive %zu joint names and %zu positions.", joint_state.name.size(), joint_state.position.size() ); //检查要读取的数据是否配对 if (joint_state.name.size() != joint_state.position.size()) { RCLCPP_ERROR(node->get_logger(), "Joint names and positions do not match."); rclcpp::shutdown(); return 1; } for (std::size_t i = 0; i < joint_state.name.size(); ++i) { RCLCPP_INFO( node->get_logger(), "%s: %.4f rad", joint_state.name[i].c_str(), joint_state.position[i]); } //创建第二个客户端: auto validity_client = node->create_client<moveit_msgs::srv::GetStateValidity>( "/check_state_validity" ); if(!validity_client->wait_for_service(std::chrono::second(5))) { RCLCPP_ERROR(node->get_logger()."State validity service not available."); rclcpp::shutdown(); return 1; } auto validity_request = std::make_shared<moveit_msgs::srv::GetStateValidity::Request>(); //把上一次获取场景时收到的机器人状态,复制进这次检查请求 validity_request->robot_state = response->scene.robot_state; validity_request->group_name = "arm"; auto validity_future = validity_client->async_send_request(validity_request); auto validity_status = rclcpp::spin_until_future_complete( node,validity_future,std::chrono::second(5)); if(validity_status != rclcpp::FutureReturnCode::SUCCESS) { RCLCPP_ERROR(node->get_logger(),"Failed to receive validity result."); rclcpp::shutdown; return 1; } auto validity_response = validity_future.get(); if(validity_response->valid) { RCLCPP_INFO(node->get_logger(),"State check PASSED"); } else { RCLCPP_WARN(node->get_logger(),"State check FAILED"); } RCLCPP_INFO( node->get_logger(), "Returned contact count: %zu", validity_response->contacts.sizes() ); rclcpp::shutdown(); return 0; }