几何尺寸与公差论坛

 找回密码
 注册
查看: 216|回复: 11

布局管道pipe,如果解决2管道自相交问题

[复制链接]
发表于 2025-3-18 14:13:22 | 显示全部楼层 |阅读模式
布局管道pipe,如果2管道自相交如何优化解决此问题,用碰撞检测(Clash Detection)
 楼主| 发表于 2025-3-18 14:14:33 | 显示全部楼层
2. CGAL (Computational Geometry Algorithms Library)

    官网:https://www.cgal.org/

    语言支持:C++

    特点:
        专业的计算几何库,支持2D/3D碰撞检测、布尔运算、网格生成等。
        高性能、数值稳定,广泛用于学术与工业界。

    适用场景:
        适合复杂几何建模、点云数据处理、BIM与EDA结合场景。

    安装:

sudo apt-get install libcgal-dev

示例: 使用AABB Tree实现碰撞检测:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <CGAL/Surface_mesh.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef CGAL::AABB_face_graph_triangle_primitive<Mesh> Primitive;
typedef CGAL::AABB_traits<K, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;

int main() {
    Mesh mesh1, mesh2;
    // 假设加载了两个BIM模型的几何体
    Tree tree1(faces(mesh1).first, faces(mesh1).second, mesh1);
    Tree tree2(faces(mesh2).first, faces(mesh2).second, mesh2);

    if (tree1.do_intersect(tree2)) {
        std::cout << "检测到碰撞!" << std::endl;
    } else {
        std::cout << "无碰撞!" << std::endl;
    }

    return 0;
}
 楼主| 发表于 2025-3-18 14:15:23 | 显示全部楼层
继续,如果使用AABB Tree检测到碰撞检测后,如何改变管道轨迹跨越阻挡管道继续生成管道?比如加个弯管?
 楼主| 发表于 2025-3-18 14:15:42 | 显示全部楼层
要在检测到管道碰撞后改变管道的轨迹,比如绕开阻挡管道继续生成,可以采用以下方法:

    添加弯管 (Elbow) 进行绕障
        检测到碰撞时,根据碰撞点和方向生成弯管(弯头)来绕开阻挡管道。
        典型的弯管包括90°、45°等,根据需要灵活生成。

    调整路径规划
        通过AABB Tree获取碰撞点和法向量,根据碰撞的深度和角度调整路径。
        使用路径规划算法(如A*、Dijkstra)生成最优路径。

    分段管道重建
        如果简单调整无法绕过阻挡物,则将原管道分段,在中间插入“绕障”结构,包括弯头或多个直管段。
 楼主| 发表于 2025-3-18 14:16:10 | 显示全部楼层
代码实现示例

我们继续使用 CGAL 的 AABB Tree 进行碰撞检测,并结合弯头管道调整来避障:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <iostream>
#include <vector>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef CGAL::AABB_face_graph_triangle_primitive<Mesh> Primitive;
typedef CGAL::AABB_traits<K, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;

struct Pipe {
    K::Point_3 start;
    K::Point_3 end;
    double radius;
};

// 生成弯头(弯管)函数
std::vector<K::Point_3> generate_elbow(const K::Point_3& collision_point, const K::Vector_3& norm_vec, const K::Point_3& end) {
    std::vector<K::Point_3> new_path;

    // 第一个折弯点:从碰撞点根据法向量偏移
    K::Point_3 elbow1 = collision_point + 0.5 * norm_vec;  // 偏移0.5个单位
    new_path.push_back(elbow1);

    // 第二个折弯点:再根据目标点调整方向
    K::Point_3 elbow2 = elbow1 + (end - collision_point) * 0.5;
    new_path.push_back(elbow2);

    new_path.push_back(end);  // 终点
    return new_path;
}

// 碰撞检测
bool detect_collision(const Tree& tree, const Pipe& pipe, K::Point_3& collision_point, K::Vector_3& collision_normal) {
    CGAL::AABB_tree<Traits>::Object_and_primitive_id obj_and_id;
    K::Segment_3 segment(pipe.start, pipe.end);

    // 检测最近碰撞
    if (tree.do_intersect(segment)) {
        auto intersection = tree.any_intersection(segment);
        if (const K::Point_3* p = boost::get<K::Point_3>(&*intersection)) {
            collision_point = *p;

            // 计算法向量,假设与障碍管道法线垂直
            collision_normal = pipe.end - pipe.start;
            collision_normal = collision_normal / std::sqrt(collision_normal.squared_length());
            return true;
        }
    }
    return false;
}

int main() {
    // 模拟障碍管道网格
    Mesh obstacle_mesh;
    Mesh::Vertex_index v0 = obstacle_mesh.add_vertex(K::Point_3(1, 1, 0));
    Mesh::Vertex_index v1 = obstacle_mesh.add_vertex(K::Point_3(1, 1, 2));
    Mesh::Vertex_index v2 = obstacle_mesh.add_vertex(K::Point_3(1, 3, 2));
    Mesh::Vertex_index v3 = obstacle_mesh.add_vertex(K::Point_3(1, 3, 0));

    obstacle_mesh.add_face(v0, v1, v2);
    obstacle_mesh.add_face(v2, v3, v0);

    // AABB树构建
    Tree tree(faces(obstacle_mesh).first, faces(obstacle_mesh).second, obstacle_mesh);

    Pipe pipe{{0, 2, 1}, {2, 2, 1}, 0.1};
    K::Point_3 collision_point;
    K::Vector_3 collision_normal;

    if (detect_collision(tree, pipe, collision_point, collision_normal)) {
        std::cout << "检测到碰撞!碰撞点:(" << collision_point << ")" << std::endl;

        // 生成弯头绕开障碍
        std::vector<K::Point_3> new_path = generate_elbow(collision_point, collision_normal, pipe.end);

        std::cout << "调整后的路径点:" << std::endl;
        for (const auto& point : new_path) {
            std::cout << "(" << point << ") ";
        }
        std::cout << std::endl;
    } else {
        std::cout << "无碰撞,路径正常。" << std::endl;
    }

    return 0;
}
 楼主| 发表于 2025-3-18 14:16:42 | 显示全部楼层
代码说明

    AABB Tree 碰撞检测
        使用 do_intersect 检测碰撞,如果碰撞,获取碰撞点和法向量。

    轨迹调整
        调用 generate_elbow 生成弯头路径。
        调整路径点以避障,支持复杂路径调整。

    参数调整
        弯头弯曲程度可调整,以适应不同场景。
        可以进一步扩展路径规划算法如A*或Dijkstra,实现更智能的绕障避碰。
 楼主| 发表于 2025-3-18 14:48:40 | 显示全部楼层
如何调整弯头弯曲程度 ,以适应不同场景?
      如何进一步扩展路径规划算法如A*或Dijkstra,实现更智能的绕障避碰?
要调整弯头的弯曲程度,并进一步扩展路径规划算法(如A*、Dijkstra)以实现更智能的绕障避碰,可以从以下几个方面入手:
&#128204; 一、调整弯头弯曲程度

调整弯头(elbow)的弯曲程度,通常是根据弯曲半径(Bend Radius)和弯曲角度(Bend Angle)来控制的。这两个参数可以灵活设置,以应对不同场景下的避障需求。
1. 调整弯曲半径

    大半径:平缓的转弯,适用于大空间,减少压力损失或流体摩擦。
    小半径:紧凑的转弯,适用于狭小空间或复杂环境。

2. 调整弯曲角度

    常见的弯头角度有 45°、90°,也可以自定义其他角度。
    根据管道避障的紧急程度调整角度大小。
例:调整弯头代码

在之前的代码基础上,改进 generate_elbow 函数,支持自定义弯曲半径和角度:

#include <vector>
#include <cmath>
#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_3 Point;
typedef K::Vector_3 Vector;

std::vector<Point> generate_elbow(const Point &collision_point, const Vector &norm_vec, const Point &end,
                                  double bend_radius, double bend_angle) {
    std::vector<Point> new_path;

    // 第一个转弯点,绕开障碍物
    Vector offset = bend_radius * norm_vec;
    Point elbow1 = collision_point + offset;
    new_path.push_back(elbow1);

    // 计算弯曲角度位置的下一个点
    double rad = bend_angle * M_PI / 180.0;  // 转为弧度制
    Vector rotation_vec = Vector(-norm_vec.y(), norm_vec.x(), 0);  // 旋转90°的向量
    rotation_vec = rotation_vec / sqrt(rotation_vec.squared_length());  // 归一化
    Vector bend_offset = bend_radius * sin(rad) * rotation_vec;
    Point elbow2 = elbow1 + bend_offset;
    new_path.push_back(elbow2);

    // 最终目标点
    new_path.push_back(end);

    return new_path;
}

int main() {
    Point collision_point(1.0, 1.0, 1.0);
    Vector norm_vec(1.0, 0.0, 0.0);  // 法向量
    Point end(3.0, 2.0, 1.0);

    double bend_radius = 0.5;
    double bend_angle = 45.0;

    auto path = generate_elbow(collision_point, norm_vec, end, bend_radius, bend_angle);
   
    std::cout << "调整后的弯头路径点:" << std::endl;
    for (const auto &p : path) {
        std::cout << "(" << p << ")" << std::endl;
    }

    return 0;
}

代码说明

    弯曲半径 (bend_radius) 和 弯曲角度 (bend_angle) 可调。
    根据法向量生成偏移,用于调整弯头的避障路径。
 楼主| 发表于 2025-3-18 14:49:23 | 显示全部楼层
二、路径规划算法扩展:A 和 Dijkstra*

使用 A* 或 Dijkstra 算法实现智能路径规划,可以在复杂场景中更智能地绕障避碰。这两个算法通常在栅格化环境或图结构中应用,适用于多障碍、多弯道的避障规划。
1. A 路径规划*

    优点:启发式算法,搜索效率更高。
    适合场景:复杂障碍、多目标点的避碰问题。
    启发函数:常用曼哈顿距离(Manhattan Distance)或欧氏距离(Euclidean Distance)。

A 代码实现*

这里以二维网格为例进行实现:

#include <iostream>
#include <vector>
#include <queue>
#include <cmath>
#include <unordered_map>

struct Node {
    int x, y;
    double cost, heuristic;
    Node *parent;

    Node(int _x, int _y, double _cost, double _heuristic, Node *_parent = nullptr)
        : x(_x), y(_y), cost(_cost), heuristic(_heuristic), parent(_parent) {}

    bool operator<(const Node &other) const {
        return (cost + heuristic) > (other.cost + other.heuristic);  // 小顶堆
    }
};

// 启发式函数(欧氏距离)
double heuristic(int x, int y, int target_x, int target_y) {
    return std::sqrt(std::pow(target_x - x, 2) + std::pow(target_y - y, 2));
}

// A* 路径规划
std::vector<std::pair<int, int>> a_star(int start_x, int start_y, int goal_x, int goal_y, const std::vector<std::vector<int>> &grid) {
    int rows = grid.size();
    int cols = grid[0].size();
    std::priority_queue<Node> pq;
    std::unordered_map<int, Node *> visited;

    pq.push(Node(start_x, start_y, 0, heuristic(start_x, start_y, goal_x, goal_y)));

    std::vector<std::pair<int, int>> directions{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

    while (!pq.empty()) {
        Node current = pq.top();
        pq.pop();

        // 到达目标
        if (current.x == goal_x && current.y == goal_y) {
            std::vector<std::pair<int, int>> path;
            Node *node = &current;

            while (node) {
                path.emplace_back(node->x, node->y);
                node = node->parent;
            }

            std::reverse(path.begin(), path.end());
            return path;
        }

        // 标记访问
        visited[current.x * cols + current.y] = new Node(current);

        // 遍历四个方向
        for (const auto &dir : directions) {
            int nx = current.x + dir.first;
            int ny = current.y + dir.second;

            // 越界或碰撞检测
            if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || grid[nx][ny] == 1) continue;

            double new_cost = current.cost + 1;
            double h = heuristic(nx, ny, goal_x, goal_y);

            if (!visited.count(nx * cols + ny)) {
                pq.push(Node(nx, ny, new_cost, h, new Node(current)));
            }
        }
    }

    return {};
}

int main() {
    std::vector<std::vector<int>> grid = {
        {0, 0, 0, 0, 0},
        {1, 1, 0, 1, 1},
        {0, 0, 0, 0, 0},
        {0, 1, 1, 1, 0},
        {0, 0, 0, 0, 0}};

    auto path = a_star(0, 0, 4, 4, grid);

    std::cout << "规划路径:" << std::endl;
    for (const auto &[x, y] : path) {
        std::cout << "(" << x << ", " << y << ") ";
    }

    return 0;
}

总结

    弯头调整:调整半径和角度,适应不同场景的避障需求。
    路径规划扩展:
        简单场景可用弯头+简单避障。
        复杂场景建议使用 A* 或 Dijkstra 算法,结合网格化路径规划。
    更复杂应用:可扩展到 3D 网格规划,结合 Open3D、CGAL 实现复杂BIM管道规划。
 楼主| 发表于 2025-3-18 14:52:59 | 显示全部楼层
继续,扩展到 3D 网格规划,结合 CGAL 实现复杂BIM管道规划,用c++编写
 楼主| 发表于 2025-3-18 14:53:17 | 显示全部楼层
要扩展到 3D 网格规划,结合 CGAL 实现复杂的 BIM 管道规划,我们可以从以下几个方面进行设计和编码:
&#128204; 3D 网格路径规划:目标与应用

    目标:
        在 BIM 环境中规划管道路径,智能避障、绕过复杂障碍物。
        支持 弯头调整 和 路径规划,实现灵活的绕障避碰。

    应用:
        碰撞检测和路径优化:在BIM软件中检测碰撞并优化路径。
        管道布线规划:复杂环境下的管道避障、连接和布局。

&#128204; 设计思路

    场景建模:
        使用 CGAL 的 AABB Tree 进行 3D 碰撞检测,确保准确避障。
        使用 3D 栅格(Grid)或 Voxel 表示可行区域与障碍物。

    路径规划:
        A* 算法:高效搜索最短路径并结合启发式函数,适合复杂避障。
        Dijkstra 算法:可以用于多目标规划或无启发式场景。

    调整弯头和弯曲程度:
        根据碰撞点和法向量调整路径,灵活调整 弯头(elbow)的半径与角度。

&#128204; 代码实现

下面的代码展示了如何在 3D 网格 上使用 CGAL 进行路径规划,包括碰撞检测和弯头调整。
&#128295; 1. 头文件和库配置

确保安装 CGAL 并配置开发环境,添加编译选项:

sudo apt-get install libcgal-dev
g++ -std=c++14 -I/usr/include/CGAL -lgmp -lmpfr -o bim_planning bim_planning.cpp

&#128295; 2. 代码实现

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <cmath>
#include <limits>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef CGAL::AABB_face_graph_triangle_primitive<Mesh> Primitive;
typedef CGAL::AABB_traits<K, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;

struct Node {
    int x, y, z;
    double cost, heuristic;
    Node *parent;

    Node(int _x, int _y, int _z, double _cost, double _heuristic, Node *_parent = nullptr)
        : x(_x), y(_y), z(_z), cost(_cost), heuristic(_heuristic), parent(_parent) {}

    bool operator<(const Node &other) const {
        return (cost + heuristic) > (other.cost + other.heuristic);
    }
};

// 启发式函数(欧氏距离)
double heuristic(int x, int y, int z, int tx, int ty, int tz) {
    return std::sqrt(std::pow(tx - x, 2) + std::pow(ty - y, 2) + std::pow(tz - z, 2));
}

// A* 路径规划
std::vector<std::tuple<int, int, int>> a_star_3d(const std::vector<std::vector<std::vector<int>>> &grid,
                                                 int sx, int sy, int sz, int gx, int gy, int gz) {
    int nx = grid.size(), ny = grid[0].size(), nz = grid[0][0].size();
    std::priority_queue<Node> pq;
    std::unordered_map<int, Node *> visited;

    pq.push(Node(sx, sy, sz, 0, heuristic(sx, sy, sz, gx, gy, gz)));

    std::vector<std::tuple<int, int, int>> directions = {
        {1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0},
        {0, 0, 1}, {0, 0, -1}};

    while (!pq.empty()) {
        Node current = pq.top();
        pq.pop();

        if (current.x == gx && current.y == gy && current.z == gz) {
            std::vector<std::tuple<int, int, int>> path;
            Node *node = ¤t;
            while (node) {
                path.emplace_back(node->x, node->y, node->z);
                node = node->parent;
            }
            std::reverse(path.begin(), path.end());
            return path;
        }

        visited[current.x * ny * nz + current.y * nz + current.z] = new Node(current);

        for (const auto &[dx, dy, dz] : directions) {
            int nx = current.x + dx, ny = current.y + dy, nz = current.z + dz;

            if (nx < 0 || nx >= grid.size() || ny < 0 || ny >= grid[0].size() ||
                nz < 0 || nz >= grid[0][0].size() || grid[nx][ny][nz] == 1)
                continue;

            double new_cost = current.cost + 1;
            double h = heuristic(nx, ny, nz, gx, gy, gz);

            if (!visited.count(nx * ny * nz + ny * nz + nz)) {
                pq.push(Node(nx, ny, nz, new_cost, h, new Node(current)));
            }
        }
    }

    return {};
}

int main() {
    // 3D网格初始化 (0 表示可通行,1 表示障碍)
    int nx = 10, ny = 10, nz = 10;
    std::vector<std::vector<std::vector<int>>> grid(nx, std::vector<std::vector<int>>(ny, std::vector<int>(nz, 0)));

    // 障碍物添加
    for (int i = 3; i < 7; ++i) {
        for (int j = 3; j < 7; ++j) {
            for (int k = 3; k < 7; ++k) {
                grid[j][k] = 1; // 障碍区
            }
        }
    }

    // A* 规划路径 (起点: (0,0,0), 终点: (9,9,9))
    auto path = a_star_3d(grid, 0, 0, 0, 9, 9, 9);

    if (!path.empty()) {
        std::cout << "规划路径点:" << std::endl;
        for (const auto &[x, y, z] : path) {
            std::cout << "(" << x << ", " << y << ", " << z << ") ";
        }
        std::cout << std::endl;
    } else {
        std::cout << "无法找到路径!" << std::endl;
    }

    return 0;
}

&#128204; 代码说明

    使用 三维栅格 进行路径规划,0 表示可通行,1 表示障碍。
    使用 A* 算法进行路径搜索,通过启发式函数提升搜索效率。
    可灵活调整栅格大小、障碍设置、起终点位置等,适应不同BIM场景。

&#128204; 总结与扩展

    路径规划:A* 算法的扩展可支持复杂场景,可以引入弯头调整和网格细化优化。
    与CGAL结合:可将 CGAL AABB Tree 结合,用于精准碰撞检测,实现更复杂的避障。
    可视化:结合 VTK 或 Open3D 进行3D Mesh和路径可视化。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|Archiver|小黑屋|几何尺寸与公差论坛

GMT+8, 2025-4-2 05:27 , Processed in 0.043496 second(s), 19 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表