ROS2中将octomap发布到Moveit中作为碰撞物体

news2024/11/17 5:23:05

1.安装准备

这里假设你已经装好了ROS2以及Moveit2(都用二进制安装就行,不用从源码安转),没有安装好的,可以按照鱼香ROS的教程安装,两三行命令就搞定了。
我的ROS2版本为humble,请根据你使用的实际版本替换。
安装pcl

sudo apt install libpcl-dev
sudo apt-get install ros-humble-pcl*

安装octomap相关库

sudo apt-get install ros-humble-octomap*

下面这个也安装一下

sudo apt-get install ros-humble-moveit-ros-perception

2.bt文件读取

octomap信息可能来自话题(由设备或者其他节点发布),或者从bt文件中读取。这里出于便于演示等原因,选择从文件中读取。文件读取时要注意使用的bt文件是txt还是bin的,要选择对应的读取方式,具体请查看代码。
这个octomap的功能还是用之前做的机械手环境来演示。
以下所使用到的【geb079.bt】文件来自于octomap的github。而【octomap.bt】文件是通过以下函数创建:

void createOctoMap()
{
    // 创建OcTree对象,并设置分辨率为0.05
    octomap::OcTree* tree = new octomap::OcTree(0.05);

    // 设置障碍物的大小和位置
    octomap::point3d origin(0, 0, 0);
    octomap::point3d obstacle(0, 0, 0); // 1 1 1
    double size = 0.2;

    // 在OcTree中添加一个立方体障碍物
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() - size, origin.y() + obstacle.y() - size, origin.z() + obstacle.z() - size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() + size, origin.y() + obstacle.y() - size, origin.z() + obstacle.z() - size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() + size, origin.y() + obstacle.y() + size, origin.z() + obstacle.z() - size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() - size, origin.y() + obstacle.y() + size, origin.z() + obstacle.z() - size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() - size, origin.y() + obstacle.y() - size, origin.z() + obstacle.z() + size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() + size, origin.y() + obstacle.y() - size, origin.z() + obstacle.z() + size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() + size, origin.y() + obstacle.y() + size, origin.z() + obstacle.z() + size), true);
    tree->updateNode(octomap::point3d(origin.x() + obstacle.x() - size, origin.y() + obstacle.y() + size, origin.z() + obstacle.z() + size), true);

    // 将OcTree保存为octomap.bt文件
    std::string filename = "octomap.bt";
    auto ret = tree->writeBinary(filename);
//    auto ret = tree->write(filename);

    qDebug() << "write octomap.bt:" << ret;
    // 删除OcTree对象
    delete tree;
}

假如要将读取、接收到的octomap通过代码的方式“输出”到moveit中,让其参与碰撞检测、路径规划,一般有两种方式:

2.1通过moveit_msgs::msg::CollisionObject加入场景中

这种方式,是通过 octomap–》pointCloud–》mesh 的方式进行的,比较麻烦。而且在pointCloud–》mesh这一步遇到了一些问题,转出来的是面片,而非实体mesh。后面再研究研究

// 这个函数还是有问题的,弄出来的是面片,而不是mesh,后续还需要改进
void pclPointCloudToShapeMsgsMesh(const pcl::PointCloud<pcl::PointXYZ> *cloud,
                                  shape_msgs::msg::Mesh &mesh)
{
    // 创建三角形索引数组
    pcl::PolygonMesh triangles;
    pcl::Vertices vertices;

    for (size_t i = 0; i < cloud->size() - 2; ++i)
    {
        vertices.vertices.clear();
        vertices.vertices.push_back(i);
        vertices.vertices.push_back(i + 1);
        vertices.vertices.push_back(i + 2);
        triangles.polygons.push_back(vertices);
    }

    // 拷贝点云数据到Mesh消息中
    mesh.vertices.resize(cloud->size());
    for (int i = 0; i < cloud->size(); ++i)
    {
        geometry_msgs::msg::Point p;
        p.x = (*cloud)[i].x;
        p.y = (*cloud)[i].y;
        p.z = (*cloud)[i].z;
        mesh.vertices[i] = p;
    }

    // 拷贝三角形索引数据到Mesh消息中
    mesh.triangles.resize(triangles.polygons.size());
    for (int i = 0; i < triangles.polygons.size(); ++i)
    {
        shape_msgs::msg::MeshTriangle t;
        t.vertex_indices[0] = triangles.polygons[i].vertices[0];
        t.vertex_indices[1] = triangles.polygons[i].vertices[1];
        t.vertex_indices[2] = triangles.polygons[i].vertices[2];
        mesh.triangles[i] = t;
    }
}

// 将octoMap作为碰撞物体导入到moveit中
void appendCollisionObject(QString octoFile)
{
    auto const node = std::make_shared<rclcpp::Node>(
                "hello_moveit",
                rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));

    //创建 MoveIt MoveGroup Interface,用于连接、获取MoveGroup的信息
    using moveit::planning_interface::MoveGroupInterface;
    auto move_group_interface = MoveGroupInterface(node, "my_group"); // 估计要依附一个节点,才能进行interface的创建

    // 将碰撞物体加到scene中
    moveit::planning_interface::PlanningSceneInterface planning_scene_interface;

    auto frameId = move_group_interface.getPlanningFrame();
    qDebug() << "frame id:" << frameId.data();
    {
        std::shared_ptr<octomap::OcTree> octree = std::make_shared<octomap::OcTree>(octomap::OcTree(octoFile.toStdString()));
        if (octree == nullptr) {
          throw std::runtime_error("Could not read Octomap data from file " + octoFile.toStdString());
        }

        // octTree转点云
        pcl::PointCloud<pcl::PointXYZ> cloud;
        // Traverse all the leaf nodes and add them to the point cloud if they are occupied
        for (auto it = octree->begin_leafs(); it != octree->end_leafs(); ++it) {
            if (octree->isNodeOccupied(*it)) {
                pcl::PointXYZ pt;
                pt.x = it.getX();
                pt.y = it.getY();
                pt.z = it.getZ();
                cloud.points.push_back(pt);
            }
        }

        // 点云转mesh
        shape_msgs::msg::Mesh output_mesh;
        pclPointCloudToShapeMsgsMesh(&cloud, output_mesh);

        geometry_msgs::msg::Pose octomap_pose;
        octomap_pose.orientation.w = 1.0;
        octomap_pose.position.x = 0.2;
        octomap_pose.position.y = 0.2;
        octomap_pose.position.z = 0.2;

        // 直接往octomap_object里面塞mesh
        moveit_msgs::msg::CollisionObject octomap_object;
        octomap_object.id = "my_octomap_object";
        octomap_object.header.frame_id = frameId;
        octomap_object.meshes.push_back(output_mesh);
        octomap_object.mesh_poses.push_back(octomap_pose);
        octomap_object.operation = moveit_msgs::msg::CollisionObject::ADD;


        // 将碰撞对象添加到collision_objects中,然后传递给applyCollisionObjects函数
        std::vector<moveit_msgs::msg::CollisionObject> collision_objects;
        collision_objects.push_back(octomap_object);
        qDebug() << "applyCollisionObjects" <<  planning_scene_interface.applyCollisionObjects(collision_objects);
    }
}

2.2通过moveit_msgs::msg::PlanningScene加入(替换)场景

目前我使用的是这种,效果良好。不过可能会存在一些冲突,等后面研究得更加透彻之后再修改吧。

void appendScene(QString octoFile)
{
    auto const node = std::make_shared<rclcpp::Node>(
                "hello_moveit1",
                rclcpp::NodeOptions().automatically_declare_parameters_from_overrides(true));


    //创建 MoveIt MoveGroup Interface,用于连接、获取MoveGroup的信息
    using moveit::planning_interface::MoveGroupInterface;
    auto move_group_interface = MoveGroupInterface(node, "my_group"); // 估计要依附一个节点,才能进行interface的创建
    auto frameId = move_group_interface.getPlanningFrame();

    // Load an OctoMap from file
    octomap::OcTree* octree = new octomap::OcTree(octoFile.toStdString());

    // 将Octomap数据转换为ROS2消息
    octomap_msgs::msg::Octomap octomap_msg;
    octomap_msgs::binaryMapToMsg(*octree, octomap_msg);

    // 创建PlanningSceneMonitor实例, 用于获取 AllowedCollisionMatrix
    auto planning_scene_monitor = std::make_shared<planning_scene_monitor::PlanningSceneMonitor>(node,
        "robot_description");
    collision_detection::AllowedCollisionMatrix acm = planning_scene_monitor->getPlanningScene()->getAllowedCollisionMatrix();
//    // 假设我们需要允许"obstacle_1"和"obstacle_2"之间的碰撞
//    acm.setEntry("obstacle_1", "obstacle_2", true);

    // 将 collision_detection::AllowedCollisionMatrix 转成 moveit_msgs::msg::AllowedCollisionMatrix
    moveit_msgs::msg::AllowedCollisionMatrix acm_msg;
    acm.getMessage(acm_msg);

    moveit_msgs::msg::PlanningScene planning_scene_msg;
    planning_scene_msg.world.octomap.header.frame_id = frameId;  // Replace "map" with your frame ID
    planning_scene_msg.world.octomap.origin.orientation.w = 1.0;
    planning_scene_msg.world.octomap.octomap = octomap_msg;
    planning_scene_msg.allowed_collision_matrix = acm_msg;

    // 将PlanningScene 加(替换)到moveit中
    moveit::planning_interface::PlanningSceneInterface planning_scene_interface;
    planning_scene_interface.applyPlanningScene(planning_scene_msg);
}

3.效果演示

视频演示可以看这里:【MoveIt2中导入octomap】
在这里插入图片描述


参考
https://blog.csdn.net/qq_27865227/article/details/125002311

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

银行数字化转型导师坚鹏:银行同业核心产品与营销策略解读

数字化背景下银行同业核心产品与营销策略解读课程背景&#xff1a; 数字化背景下&#xff0c;很多银行存在以下问题&#xff1a; 不清楚银行同业核心产品发展现状&#xff1f; 不清楚如何银行同业产品营销策略&#xff1f; 不知道如何更好地挖掘他行优质客户&#xff1f; 课…

国产化系统改造实践(未完)

一、项目背景 2020 年,红帽公司宣布,将在 2021 年 12 月 31 日和 2024 年 6 月 30 日分别终止对 CentOS 8 和 CentOS 7 的服务支持,把 CentOS 项目的工作和投资集中在CentOS Stream 上。 CentOS Linux 8已于2021年12月31日停止维护,CentOS Linux7也 将于2024年6月停服。s所…

技术创业者必读:从验证想法到技术产品商业化的全方位解析

导语 | 技术创业之路往往充满着挑战和不确定性&#xff0c;对于初入创业领域的人来说&#xff0c;如何验证自己的创业想法是否有空间、如何选择靠谱的投资人、如何将技术产品商业化等问题都需要认真思考和解决。在「TVP 技术夜未眠」第六期直播中&#xff0c;正马软件 CTO、腾讯…

【《C Primer Plus》读书笔记】第17章:高级数据表示

【《C Primer Plus》读书笔记】第17章&#xff1a;高级数据表示17.1 研究数据表示17.2 从数组到链表17.3 抽象数据类型&#xff08;ADT&#xff09;17.4 队列ADT17.5 用队列进行模拟17.6 链表和数组17.7 二叉查找树17.8 其他说明17.1 研究数据表示 在开始编写代码之前&#xf…

【2023】前端JWT详解

概述 回顾登录的流程&#xff1a; 接下来的问题是&#xff1a;这个出入证&#xff08;令牌&#xff09;里面到底存啥&#xff1f; 一种比较简单的办法就是直接存储用户信息的JSON串&#xff0c;这会造成下面的几个问题&#xff1a; 非浏览器环境&#xff0c;如何在令牌中记录…

C/C++每日一练(20230416)

目录 1. 求数列第n项值 ※ 2. 整数转换英文表示 &#x1f31f;&#x1f31f;&#x1f31f; 3. 数组中找出最大值及索引位置 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 求数…

微信小程序引入骨架屏

骨架屏的使用越来越广泛。在微信小程序中使用骨架屏如果自己实现&#xff0c;不同的页面对应不同的骨架屏&#xff0c;会有一定难度&#xff1b;不过&#xff0c;微信小程序已经提供生成骨架屏功能&#xff0c;使得我们在开发中非常方便&#xff0c;下面介绍如何生成 在生成骨架…

[Linux]管理用户和组

​⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;Linux基础操作。本文主要是分享一些Linux系统常用操作&#xff0c;内容主要来源是学校作业&#xff0c;分享出来的…

JavaEE 协议 信息是如何在一个机器传到另一个机器的(理论)

抓住你了&#xff01; 文章目录JavaEE & 协议 & 信息是如何在一个机器传到另一个机器的1. 局域网2. 广域网3. IP与端口号&#xff08;初识&#xff09;4. 协议4.1 协议分类分层4.2 协议分层的好处4.3 真实的网络协议分层&#xff0c;TCP/IP五层网络模型4.3.1 应用层4.3…

Spark 3.0中 Spark SQL优化

在Spark3.x版本提供Adaptive Query Execution自适应查询技术&#xff0c;通过在”运行时”对查询执行计划进行优化&#xff0c;允许Planner在运行时执行可选计划&#xff0c;这些可选计划将会基于运行时数据统计进行动态优化&#xff0c;从而提高性能。 Adaptive Query Execut…

在DongshanPI-D1开箱使用分享与折腾记录实现MPU6050数据读取

前言 上一篇文章使用RT-Smart的IIC驱动OLED屏幕&#xff0c;进行基本的字符串显示,在使用过程中对RT-Smart有了一定熟悉&#xff0c;准备使用SPI驱动ST7789&#xff0c;但SPI接口没有引出&#xff0c;本次使用手上已有的传感器MPU6050进行使用。 过程 本次直接开始添加离线包…

NDK RTMP直播客户端二

在之前完成的实战项目【FFmpeg音视频播放器】属于拉流范畴&#xff0c;接下来将完成推流工作&#xff0c;通过RTMP实现推流&#xff0c;即直播客户端。简单的说&#xff0c;就是将手机采集的音频数据和视频数据&#xff0c;推到服务器端。 接下来的RTMP直播客户端系列&#xff…

在国内pmp证书有什么含金量?

关于PMP的含金量&#xff0c;很多回答的说法都差不多&#xff0c;但那也只是字面上的含金量&#xff0c;真正的含金量还是得自己考过了之后能够给自己带来的帮助才方可对PMP含金量下定义&#xff0c;但能一眼就能看到的含金量是在一些招聘信息上关于PMP证书的要求&#xff0c;下…

【Axure教程】日期时间下拉列表

在系统中&#xff0c;我们经常会用到日期时间选择器&#xff0c;它同时包含了日历日期的选择和时间的选择&#xff0c;一般是下拉列表的形式进行选择。 今天作者就教大家如何在Axure中用中继器制作真实日期时间效果的下拉列表选。 一、效果展示 1、点击控件&#xff0c;可以…

游戏开发学习路线图(2023最新版)建议收藏

游戏开发是一个高度技术化的领域&#xff0c;需要掌握编程语言和编程技能。你可以从学习基本的编程概念和语法开始&#xff0c;如C、C#、Python等常用的游戏编程语言。掌握编程的基础知识是游戏开发的基石。很多小伙伴不知道怎么学习游戏开发&#xff0c;那么今天&#xff0c;就…

c/c++:windows平台下依赖的动态库,c底层是汇编语言,程序断点调试,反汇编,vs快捷键

c/c&#xff1a;windows平台下依赖的动态库&#xff0c;c底层是汇编语言&#xff0c;程序断点调试&#xff0c;反汇编&#xff0c;vs快捷键 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知…

【多媒体】多媒体架构

多媒体架构 首先,多媒体架构层通常由三个主要层次组成: 应用层:负责媒体的展示、控制和交互等功能,如播放器、视频编辑器等。应用层一般是用户最直接接触到的界面。 中间件层:提供了各种媒体操作的基本服务,如编解码、音频合成、图像处理、网络传输、数据存储等。中间件…

【图像分割】Meta分割一切(SAM)模型环境配置和使用教程

注意&#xff1a;python>3.8, pytorch>1.7,torchvision>0.8 Feel free to ask any question. 遇到问题欢迎评论区讨论. 官方教程&#xff1a; https://github.com/facebookresearch/segment-anything 1 环境配置 1.1 安装主要库&#xff1a; &#xff08;1&…

day08_数组

今日内容 零、 复习昨日 一、作业 二、数组 零、 复习昨日 方法/函数是什么? 方法就是完成某个功能的一段代码的集合例如: 方法是 做饭 方法的作用是什么? 封装的思想方便维护方便复用 方法的参数列表是什么意思? 参数:方法执行所需的数据参数列表: 方法执行可以需要多个数据…

Linux下C/C++ SNTP网络时间协议实现

对于许多应用程序&#xff0c;特别是在小型计算机和微控制器上&#xff0c;不需要NTP的最终性能。便开发了简单网络时间协议&#xff08;SNTP&#xff09;&#xff0c;为功能较弱的计算机提供时钟同步&#xff0c;而这些计算机不需要NTP的复杂性。 而简单网络时间协议&#xf…