插值与拟合(3):B样条曲线

在路径规划问题中,通常会用到B样条来平滑路径,本文实现并封装了三次准均匀开放B样条曲线,供大学学习使用。作者提供了三套代码方案。可以用于不同平台:方案1:MATLAB;方案2:标准C++;方案3:在ROS-noetic平台中基于C++实现,并基于Rviz实现可视化。

本文的代码有如下优势:1、即拿即用。程序员仅仅需要提供控制点(control_node)、曲线节点数量(也就是很多博客中提到的u的尺寸),通过两三行代码即可实现B样条;2、可以适用于二维、三维环境。

注意:本文侧重于代码分享,因此对于B样条曲线的原理不做阐述。(网上有很多讲的非常好的博客,作者理论阐述能力有限,故不再班门弄斧)。

提示:关于“三次”“准均匀”“开放(open)”三个前缀,大家可以搜一搜了解即可。大多数场景下我们说的B样条就是“三次”“准均匀”“开放(open)”B样条曲线。(这句话不一定严谨,个人想法)

下面开始代码分享:

1 基于MATLAB实现B样条曲线

如下图所示,程序员需要修改的主要是:第4行control_node以及第12行path_node_num的值。

clc, clear, close allcontrol_node = [0 0; 1 -2; 2 2; 3 -1; 4 1; 5 0]; % 二维坐标
% control_node = [0 0 0; 
%                 1 -2 -2; 
%                 2 2 3; 
%                 3 -1 4; 
%                 4 1 5; 
%                 5 0 5];path_node_num = 200;n = size(control_node,1)-1; % n+1个控制点
degree = 3; % 最高次幂3
order = 4;  % 阶数4; order = degree + 1;u = linspace(0, 1-0.0005, path_node_num);  % !!!注意:如果u的范围是[0, 1],下面节点向量的范围也要是0到1
path = zeros(length(u), size(control_node, 2));%% 生成节点向量kontsVector
m = n+1 + degree ;
knotsVector = zeros(n+1+degree+1, 1);
for i= 1:m+1if i <= degree + 1knotsVector(i) = 0;endif i>degree+1 && i<=m-degreeknotsVector(i) = knotsVector(i-1)+1;endif i > m-degreeknotsVector(i) = knotsVector(m-degree)+1;end
end
knotsVector = knotsVector / knotsVector(m+1);%% 获取准均匀B样条曲线
for idx = 1 : length(u)for i = 1 : size(control_node, 1)Bik = BaseFunction(i, order-1 , u(idx), knotsVector);path(idx,:) = path(idx,:) + Bik*control_node(i,:);end
endplot(path(:,1), path(:,2),'g--','LineWidth',5);hold on
scatter(control_node(:,1), control_node(:,2),500, '.');function Bik_u = BaseFunction(i, k , u, NodeVector)if k == 0       % 0次B样条if u >= NodeVector(i) && u < NodeVector(i+1)Bik_u = 1;elseBik_u = 0;end
elseLength1 = NodeVector(i+k) - NodeVector(i);Length2 = NodeVector(i+k+1) - NodeVector(i+1);      % 支撑区间的长度if Length1 == 0       % 规定0/0 = 0Length1 = 1;endif Length2 == 0Length2 = 1;endBik_u = (u - NodeVector(i)) / Length1 * BaseFunction(i, k-1, u, NodeVector) ...+ (NodeVector(i+k+1) - u) / Length2 * BaseFunction(i+1, k-1, u, NodeVector);
end
end

下面两张图分别是二维和三维环境下的效果:

2 基于标准C++实现B样条曲线

主要包括bspline.h、bspline.cpp以及main.cpp三个代码文件。主要需要在main.cpp中进行三处修改:

第7行:Eigen::MatrixXd control_node(6, 2);其中6表示控制点数量,2表示维度,这两个参数供程序员修改;

第8行:程序员提供控制点

第14行:提供path_node_num,即路径节点数量。

2.1 首先是bspline.h

#ifndef __BSPLINE_H__
#define __BSPLINE_H__#include <iostream>
#include <eigen3/Eigen/Eigen>
#include <vector>class Bspline
{
public:Bspline(){};~Bspline(){};/* 启动B样条 */void Bspline_start();/* 获取控制点、最高次幂、离散点的数量*/Bspline(Eigen::MatrixXd control_node, int degree, int num);/* 生成节点向量 */void get_konts();/* 获取准均匀B样条 */void get_bspline_curve();/* 基函数 */double BaseFunction(int i, int k, double u, std::vector<double> konts_vector);
private:Eigen::MatrixXd m_control_node;Eigen::MatrixXd m_path;// double int m_degree, m_order, m_num, m;std::vector<double> konts_vector;std::vector<double> u;
};#endif

2.2 之后是bspline.cpp

#include "bspline.h"Bspline::Bspline(Eigen::MatrixXd control_node, int degree, int num)
{this->m_control_node = control_node;        // 获取控制点this->m_degree = degree;                    // B样条的次数this->m_order = this->m_degree + 1;         // B样条的阶数this->m_num = num;                          // 路径点的数量int n = this->m_control_node.rows() - 1;    // n+1个控制点this->m = n + 1 + this->m_degree;           // m + 1 = n + 1 + degree + 1// 确定uthis->u.resize(this->m_num);for(int i=0; i<m_num; i++){u[i] = 0 + (1.0-0.005)/(m_num-1) * i;}this->m_path.setZero(this->m_num, this->m_control_node.cols());// 初始化路径
}void Bspline::Bspline_start()
{this->get_konts();this->get_bspline_curve();
}void Bspline::get_konts()
{this->konts_vector.resize(this->m + 1);for(int i = 0; i<=m; i++){if(i <= this->m_degree){this->konts_vector[i] = 0;}if(i > this->m_degree && i< (this->m - this->m_degree)) {this->konts_vector[i] = this->konts_vector[i-1]+1;}if(i >= (this->m - this->m_degree)){this->konts_vector[i] = this->konts_vector[this->m - this->m_degree -1] + 1;}}for(int i=0; i<=m; i++){this->konts_vector[i] = this->konts_vector[i] / this->konts_vector[m];}
}void Bspline::get_bspline_curve()
{double Bik;for(int idx = 0; idx<this->u.size(); idx++){for(int i=0; i<this->m_control_node.rows(); i++){Bik = BaseFunction(i, this->m_degree, this->u[idx], this->konts_vector);for(int j=0; j<this->m_control_node.cols(); j++){this->m_path(idx,j) = this->m_path(idx,j) + Bik*this->m_control_node(i,j);}}}// std::cout << this->m_path ;
}double Bspline::BaseFunction(int i, int k, double u, std::vector<double> konts_vector)
{double Bik_u;if(k==0){if(u>=konts_vector[i] && u<konts_vector[i+1])return Bik_u = 1.0;elsereturn Bik_u = 0.0;}else{double length1 = konts_vector[i+k] - konts_vector[i];double length2 = konts_vector[i+k+1] - konts_vector[i+1];if(length1==0){length1=1;}if(length2==0){length2=1;}return Bik_u = (u - konts_vector[i])/length1 * BaseFunction(i,k-1,u,konts_vector) + (konts_vector[i+k+1]-u) / length2 * BaseFunction(i+1,k-1,u,konts_vector);}
}

2.3 最后是main.cpp

#include <iostream>
#include "bspline.h"
#include <eigen3/Eigen/Eigen>int main()
{Eigen::MatrixXd control_node(6, 2);control_node << 0, -2,10, -2,25, -2,25, 0,40, 0,50, 0;int path_node_num = 200;Bspline bspline(control_node, 3, path_node_num);bspline.Bspline_start();return 0;
}

3 ROS-noetic+Rviz+C++实现B样条曲线

这一部分主要包括bspline.h、bspline.cpp以及main.cpp三个代码文件。其中,bspline.h、bspline.cpp跟标准C++代码完全一致,读者复制粘贴即可,因此只提供main.cpp文件,内容如下。可供修改的地方主要是三点:

第16行:Eigen::MatrixXd control_node(6, 2);其中6表示控制点数量,2表示维度,这两个参数供程序员修改;注意如果是二维,要把第29行 cloud.points[i].z 设置为0.0。

第17行:程序员提供控制点

第29行:提供path_node_num,即路径节点数量。

注意:这段代码需要读者有基本的ROS和Rviz知识。

#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl/point_cloud.h>
#include <pcl_conversions/pcl_conversions.h>
#include <eigen3/Eigen/Eigen>
#include "bspline.h"
#include <vector>int main(int argc, char *argv[])
{ros::init(argc, argv, "main_node");ros::NodeHandle nh;ros::Publisher pcl_pub = nh.advertise<sensor_msgs::PointCloud2>("path", 10);/* 将路径节点传给cloud */Eigen::MatrixXd control_node(6, 3);control_node << 0, -2, -2,1, -2, 4,2.5, -2, 1,2.5, 0, 0,4.0, 0, 2,5.0, 0, 0;   // 3-dim 地图// control_node << 0, 0,//                 1, 4,//                 2, 2,//                 3, 3,//                 4.0, 1,//                 5.0, 5;   // 2-dim 地图int path_node_num = 200;     // 散点数量Bspline bspline(control_node, 3, 200);Eigen::MatrixXd path = bspline.Bspline_start();pcl::PointCloud<pcl::PointXYZ> cloud;cloud.points.resize(path.rows());for(size_t i=0; i<cloud.points.size(); i++){cloud.points[i].x = path(i,0);cloud.points[i].y = path(i,1);cloud.points[i].z = path(i,2);}sensor_msgs::PointCloud2 output;pcl::toROSMsg(cloud, output);output.header.frame_id = "map";output.header.stamp = ros::Time::now();while(ros::ok()){   ROS_INFO("path pub...");pcl_pub.publish(output);ros::spinOnce();}return 0;
}

既然涉及到ROS,还需要配置一下CMakeLists文件,配置如下:

cmake_minimum_required(VERSION 3.0.2)
project(pcl_pkg)find_package(catkin REQUIRED COMPONENTSpcl_rosroscpprospysensor_msgsstd_msgs
)include_directories(
# include${catkin_INCLUDE_DIRS}${CMAKE_CURRENT_SOURCE_DIR}/include
)add_executable(main_node src/main.cpp src/bspline.cpp) 
target_link_libraries(main_node ${catkin_LIBRARIES} )

下面两张图分别是Rviz中二维和三维环境下的效果:

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

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

相关文章

[免费]基于Python豆瓣电影数据分析及可视化系统(Flask+echarts+pandas)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的于Python豆瓣电影数据分析及可视化系统(Flaskechartpandas)【论文源码SQL脚本】&#xff0c;分享下哈。项目介绍随着如今电影越来越多&#xff0c;各种各样的烂片和捞钱的商业片也层出不穷&#xff0c;而有意…

SQL127 月总刷题数和日均刷题数

SQL127 月总刷题数和日均刷题数 withtemp as (selectDATE_FORMAT(submit_time, "%Y%m") as submit_month,count(question_id) as month_q_cnt,round(count(question_id) / day(last_day(max(submit_time))),3) as avg_day_q_cntfrompractice_recordwhereyear(submit…

unity luban接入

1.找到luban官网并下载他的例子和.net8.0的sdk安装 官网地址如下 快速上手 | Luban 参考大佬教程如下 Luban新版本接入教程_哔哩哔哩_bilibili 2.找到他的luban_examples-main示例下的两个文件MiniTemplate和tool 3.MiniTemplate这个文件复制一份到项目工程下&#xff0c;自…

Django服务开发镜像构建

最后完整的项目目录结构1、安装依赖pip install django django-tables2 django-filter2、创建项目和主应用django-admin startproject configcd configpython manage.py startapp dynamic_models3、配置settings.py将项目模块dynamic_models加入进来&#xff0c;django_tables2…

20250706-3-Docker 快速入门(上)-常用镜像管理命令_笔记

一、配置加速器&#xfeff;1. Docker Hub简介与地址&#xfeff;公共镜像仓库: 由Docker公司维护的公共镜像仓库&#xff0c;包含大量容器镜像默认下载源: Docker工具默认从这个公共镜像库下载镜像访问地址: https://hub.docker.com镜像搜索功能: 可通过浏览器访问图形化管理系…

【unity游戏开发——优化篇】使用Occlusion Culling遮挡剔除,只渲染相机视野内的游戏物体提升游戏性能

注意&#xff1a;考虑到优化的内容比较多&#xff0c;我将该内容分开&#xff0c;并全部整合放在【unity游戏开发——优化篇】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言实战1、确保所有静止的3D物体都标记为Occluder Static静态遮挡体和Occludee …

通用业务编号生成工具类(MyBatis-Plus + Spring Boot)详解 + 3种调用方式

在企业应用开发中&#xff0c;我们经常需要生成类似 BZ -240704-0001 这种“业务编号”&#xff0c;它通常具有以下特点&#xff1a;前缀&#xff1a;代表业务类型&#xff0c;如 BZ 表示包装日期&#xff1a;年月日格式&#xff0c;通常为 yyMMdd序列号&#xff1a;当天内递增…

前端相关性能优化笔记

1.打开速度怎么变快 - 首屏加载优化2.再次打开速度怎么变快 - 缓存优化了3.操作怎么才顺滑 - 渲染优化4.动画怎么保证流畅 - 长任务拆分2.1 首屏加载指标细化:1.FP(First Paint 首次绘制) 2.FCP(First contentful Paint 首次内容绘制)&#xff0c;FP 到 FCP 中间其实主要是 SPA…

7.7晚自习作业

实操作业02&#xff1a;Spark核心开发 作业说明 请严格按照步骤操作&#xff0c;并将最终结果文件&#xff08;命名为&#xff1a;sparkcore_result.txt&#xff09;于20点前上传。结果文件需包含每一步的关键命令执行结果文本输出。 一、数据读取与转换操作 上传账户数据$…

手机FunASR识别SIM卡通话占用内存和运行性能分析

手机FunASR识别SIM卡通话占用内存和运行性能分析 --本地AI电话机器人 上一篇&#xff1a;手机无网离线使用FunASR识别SIM卡语音通话内容 下一篇&#xff1a;手机通话语音离线ASR识别商用和优化方向 一、前言 书接上一文《阿里FunASR本地断网离线识别模型简析》&#xff0c;…

虚幻引擎Unreal Engine5恐怖游戏设计制作教程,从入门到精通从零开始完整项目开发实战详细讲解中英字幕

和大家分享一个以前收集的UE5虚幻引擎恐怖游戏开发教程&#xff0c;这是国外一个大神制作的视频教程&#xff0c;教程从零开始到制作出一款完整的游戏。内容讲解全面&#xff0c;如蓝图基础知识讲解、角色控制、高级交互系统、高级库存系统、物品检查、恐怖环境氛围设计、过场动…

多人协同开发时Git使用命令

拉取仓库代码 # 拉取远程仓库至本地tar_dir路径 git clone gitgithub.com:your-repo.git target_dir # 默认是拉取远程master分支&#xff0c;下面拉取并切换到自己需要开发的分支上 # 假设自己需要开发的分支是/feature/my_branch分支 git checkout -b feature/my_branch orig…

线性表——双向链表

线性表——双向链表1. 双向链表的实现1.1 简单图例1.2 结点的定义1.3 新结点的创建1.4 链表的初始化1.5 结点的插入1.5.1 头部插入&#xff08;头插&#xff09;1.5.2 尾部插入&#xff08;尾插&#xff09;1.5.3 任意位置&#xff08;前&#xff09;插入1.6 结点的删除1.6.1 头…

Java后端技术博客汇总文档

文章目录 前言Java后端汇总链接Java基础知识点数据结构算法&#xff08;Java实现&#xff09;算法知识点合集算法刷题算法竞赛AcWing课程蓝桥杯AB组辅导课合集&#xff08;更新中…&#xff09; 源码分析redission 数据库SQL ServerMySQLRedis -Canal JUC并发编程JVMNetty日志框…

QT 菜单栏设计使用方法

目录 常用设置函数 多个QAction的单选设置 ​​​​​​​菜单相关类 ​​​​​​​ 系统菜单的生成和响应 使用代码添加系统菜单 使用UI设计器设计系统菜单 使用Qt设计及界面时&#xff0c;常用的两种方式添加菜单&#xff0c;第一使用UI界面添加&#xff0c;第二种 在…

AIGC领域AI艺术,打造个性化艺术作品

AIGC领域AI艺术,打造个性化艺术作品 关键词:AIGC、AI艺术、生成对抗网络、个性化创作、深度学习、艺术风格迁移、创意计算 摘要:本文深入探讨了AIGC(人工智能生成内容)在艺术创作领域的应用,重点分析了如何利用AI技术打造个性化艺术作品。文章从技术原理出发,详细解析了生…

基于Flask+Jinja2的快捷教务系统(后端链接到新版正方教务系统)

快捷教务系统&#xff08;Easy Educational Administration Management System, EasyEAMS&#xff09; 项目简介 EasyEAMS 是一个基于 Flask Jinja2 的现代化教务系统 Web 应用。学生可通过网页端登录&#xff0c;在线查询个人信息、成绩、课表、学业生涯、通知、选课等。系…

EDM自动化与出海独立开发实用教程

随着互联网全球化发展&#xff0c;越来越多的独立开发者&#xff08;Indie Developer&#xff09;选择将自己的产品推向海外市场。如何高效地获客、激活用户、提升转化率&#xff0c;成为出海过程中必须解决的问题。EDM&#xff08;电子邮件营销&#xff09;自动化&#xff0c;…

「日拱一码」017 深度学习常用库——TensorFlow

目录 基础操作 张量操作&#xff1a; tf.constant 用于创建常量张量 tf.Variable 用于创建可训练的变量张量 tf.reshape 可改变张量的形状 tf.concat 可将多个张量沿指定维度拼接 tf.split 则可将张量沿指定维度分割 数学运算&#xff1a; tf.add 张量的加运算 tf.su…

ARM DStream仿真器脚本常用命令

以下是ARM DStream仿真器脚本中常用的命令及其功能分类&#xff0c;结合调试流程和典型应用场景整理&#xff1a; ⚙️ 一、连接与初始化命令 connect 建立与目标设备的连接&#xff0c;需指定接口类型&#xff08;如JTAG/SWD&#xff09;和处理器核心。 示例&#xff1a;conne…