机器人C++库(12)Robotics Library 之指定路径规划算法

news2025/7/13 0:32:58

机器人C++库(12)Robotics Library 之路径规划算法:PRM、RRT、EET算法

RL库的运动规划(rl::plan)模块集成了以下经典的路径规划算法:

  • PRM算法:概率路线图算法
  • RRT算法:快速探索随机树算法
  • EET算法:搜索树算法-基于采样:https://blog.csdn.net/yohnyang/article/details/127783244

另外,补充一个开源运动规划库OMPL:https://ompl.kavrakilab.org/index.html

下边参考官方给出来的例程讲述路径规划算法原理及使用,规划效果如下图所示:

在这里插入图片描述

左图是PRM算法进行路径规划的结果,灰色路径是拟生成所有路径,绿色路径是最优路径;右图是采用EET搜索树算法进行规划的结果,绿色球球心连接起点与终点的最短路径即为最优路径。

1.PRM算法

概率路线图(Probabilistic Roadmap,PRM)属于综合查询方法,其步骤如下:

  • Step1:预处理
    在这里插入图片描述
  • Step2:搜索
    采用图搜索算法对无向图G进行搜索(A*搜索),如果能找到起始点A到终点B的路线,说明存在可行的运动规划方案。
    在这里插入图片描述
  • RL库实现:

#include <iostream>
#include <memory>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <rl/mdl/Kinematic.h>
#include <rl/math/Unit.h>
#include <rl/plan/KdtreeNearestNeighbors.h>
#include <rl/plan/Prm.h>  //1
#include<rl/plan/Rrt.h>
#include <rl/plan/RecursiveVerifier.h>
#include <rl/plan/SimpleModel.h>
#include <rl/plan/SimpleOptimizer.h>
#include <rl/plan/UniformSampler.h>
#include <rl/sg/Model.h>

#define RL_SG_BULLET 1

#ifdef RL_SG_BULLET
#include <rl/sg/bullet/Scene.h>
#endif // RL_SG_BULLET
#ifdef RL_SG_FCL
#include <rl/sg/fcl/Scene.h>
#endif // RL_SG_FCL
#ifdef RL_SG_ODE
#include <rl/sg/ode/Scene.h>
#endif // RL_SG_ODE
#ifdef RL_SG_PQP
#include <rl/sg/pqp/Scene.h>
#endif // RL_SG_PQP
#ifdef RL_SG_SOLID
#include <rl/sg/solid/Scene.h>
#endif // RL_SG_SOLID

int main()
{
	std::string scene_path = "./puma560/unimation-puma560_boxes.xml";
	std::string model_path = "./puma560/unimation-puma560.xml";

	//末端位姿
	float X0 = 0;
	float Y0 = 0;
	float Z0 = 0;
	float R = 90;
	float P = 0;
	float Y = 0;

	std::vector<rl::math::Real>angles0{ 90,-180,90,0,0,0 };
	std::vector<rl::math::Real>angles1{ 0,0,90,0,0,0 };

	//定义场景碰撞检测模型
	std::shared_ptr<rl::sg::Scene> scene;
	scene = std::make_shared<rl::sg::bullet::Scene>();


	//读入场景图像
	scene->load(scene_path);
	//读入机械臂模型
	std::shared_ptr<rl::kin::Kinematics> kinematics(rl::kin::Kinematics::create(model_path));

	//word为末端位姿?
	rl::math::Transform world = rl::math::Transform::Identity();

	world = rl::math::AngleAxis(R * ::rl::math::DEG2RAD, ::rl::math::Vector3::UnitZ())
		* ::rl::math::AngleAxis(P * ::rl::math::DEG2RAD, ::rl::math::Vector3::UnitY())
		* ::rl::math::AngleAxis(Y * ::rl::math::DEG2RAD, ::rl::math::Vector3::UnitX());

	world.translation().x() = X0;
	world.translation().y() = Y0;
	world.translation().z() = Z0;

	kinematics->world() = world;

	rl::plan::SimpleModel model;
	model.kin = kinematics.get();
	model.model = scene->getModel(0);
	model.scene = scene.get();

	rl::plan::KdtreeNearestNeighbors nearestNeighbors(&model);
	rl::plan::Prm planner;
	
	rl::plan::UniformSampler sampler;
	rl::plan::RecursiveVerifier verifier;

	sampler.seed(0);

	planner.model = &model;
	planner.setNearestNeighbors(&nearestNeighbors);
	planner.sampler = &sampler;
	planner.verifier = &verifier;

	sampler.model = &model;

	verifier.delta = 1.0f * rl::math::DEG2RAD;
	verifier.model = &model;

	rl::math::Vector start(kinematics->getDof());

	for (std::size_t i = 0; i < kinematics->getDof(); ++i)
	{
		start(i) = boost::lexical_cast<rl::math::Real>(angles0[i]) * rl::math::DEG2RAD;
	}

	planner.start = &start;
	rl::math::Vector goal(kinematics->getDof());

	for (std::size_t i = 0; i < kinematics->getDof(); ++i)
	{
		goal(i) = boost::lexical_cast<rl::math::Real>(angles1[i]) * rl::math::DEG2RAD;
	}

	planner.goal = &goal;
	planner.duration = std::chrono::seconds(20);

	std::cout << "construct() ... " << std::endl;;
	std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
	planner.construct(15);
	std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now();
	std::cout << "construct() " << std::chrono::duration_cast<std::chrono::duration<double>>(stopTime - startTime).count() * 1000 << " ms" << std::endl;

	std::cout << "solve() ... " << std::endl;;
	startTime = std::chrono::steady_clock::now();
	bool solved = planner.solve();
	stopTime = std::chrono::steady_clock::now();
	std::cout << "solve() " << (solved ? "true" : "false") << " " << std::chrono::duration_cast<std::chrono::duration<double>>(stopTime - startTime).count() * 1000 << " ms" << std::endl;

	std::cout << "NumVertices: " << planner.getNumVertices() << "  NumEdges: " << planner.getNumEdges() << std::endl;
	
	//读取路径
	rl::plan::VectorList paths = planner.getPath();
	/*rl::plan::VectorList::const_iterator i = paths.begin();
	for (; i != paths.end(); ++i)
	{
		std::cout << *i*rl::math::RAD2DEG << std::endl;
	}*/
	//插值
	rl::plan::VectorList path_;
	rl::plan::VectorList::iterator i = paths.begin();
	rl::plan::VectorList::iterator j = ++paths.begin();
	rl::math::Real length = 0;
	rl::math::Real delta = 0.05;
	for (; i != paths.end() && j != paths.end(); ++i, ++j)
	{
		length += model.distance(*i, *j);
		std::cout << "*i = " << (*i).transpose() * rl::math::RAD2DEG << std::endl << "   , *j = " << (*j).transpose() * rl::math::RAD2DEG << std::endl;

		rl::math::Real steps = std::ceil(model.distance(*i, *j) / delta);
		rl::math::Vector inter(model.getDofPosition());
		for (std::size_t k = 1; k < steps + 1; ++k)
		{
			model.interpolate(*i, *j, k / steps, inter);
			std::cout << "k = " << k << ":  " << inter.transpose() * rl::math::RAD2DEG << std::endl;
			path_.push_back(inter);
		}
	}
	std::cout << "length = " << length << std::endl;

	if (solved)
	{
		/*if (boost::lexical_cast<std::size_t>(argv[4]) >= planner.getNumVertices() &&
			boost::lexical_cast<std::size_t>(argv[5]) >= planner.getNumEdges())*/
		if (17 >= planner.getNumVertices() &&
			16 >= planner.getNumEdges())
		{
			return EXIT_SUCCESS;
		}
		else
		{
			std::cerr << "NumVertices and NumEdges are more than expected for this test case.";
			return EXIT_FAILURE;
		}
	}

	return EXIT_FAILURE;
}

2.RRT算法

RT 算法(快速扩展随机树,rapidly exploring random tree)是一种随机性算法,它可以直接应用于非完整约束系统的规划,不需进行路径转换,所以它的算法复杂度较小,尤为适用于高维多自由度的系统。

  • 缺点是得到的路径质量不是很好。需要后处理进一步优化。
    思想是快速扩张一群像树一样的路径以探索(填充)空间的大部分区域,伺机找到可行的路径。
  • RRT 的基本步骤是:
      1. 起点作为一颗种子,从它开始生长枝丫;
      2. 在机器人的“构型”空间中,生成一个随机点X;
      3. 在树上找到距离X最近的那个点,记为Y吧;
      4. 朝着X的方向生长,如果没有碰到障碍物就把生长后的树枝和端点添加到树上,返回 2;

请添加图片描述

  • 伪代码:
function BuildRRT(qinit, K, Δq)
    T.init(qinit)
    for k = 1 to K
        qrand = Sample()  -- chooses a random configuration
        qnearest = Nearest(T, qrand) -- selects the node in the RRT tree that is closest to qrand
        if  Distance(qnearest, qgoal) < Threshold then
            return true
        qnew = Extend(qnearest, qrand, Δq)  -- moving from qnearest an incremental distance in the direction of qrand
        if qnew ≠ NULL then
            T.AddNode(qnew)
    return false
 
 
function Sample() -- Alternatively,one could replace Sample with SampleFree(by using a collision detection algorithm to reject samples in C_obstacle
    p = Random(0, 1.0)
    if 0 < p < Prob then
        return qgoal
    elseif Prob < p < 1.0 then
        return RandomNode()

3.EET算法

提出一种基于工作空间信息的在探索和开发之间逐步转换的机制,详见:https://blog.csdn.net/yohnyang/article/details/127783244

  • RL库算法实现:这个算法模型比较复杂,参数比较多,调参对算法结果影响很大
#include <iostream>
#include <memory>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
//#include <rl/kin/Kinematics.h>
#include <rl/mdl/Kinematic.h>
#include <rl/math/Unit.h>
#include <rl/plan/DistanceModel.h>
#include <rl/plan/Eet.h>
#include <rl/plan/LinearNearestNeighbors.h>
#include <rl/plan/UniformSampler.h>
#include <rl/plan/WorkspaceSphereExplorer.h>
#include<rl/mdl/XmlFactory.h>
#include <rl/sg/Model.h>

#define RL_SG_BULLET 1

#ifdef RL_SG_BULLET
#include <rl/sg/bullet/Scene.h>
#endif // RL_SG_BULLET
#ifdef RL_SG_FCL
#include <rl/sg/fcl/Scene.h>
#endif // RL_SG_FCL
#ifdef RL_SG_PQP
#include <rl/sg/pqp/Scene.h>
#endif // RL_SG_PQP
#ifdef RL_SG_SOLID
#include <rl/sg/solid/Scene.h>
#endif // RL_SG_SOLID

int main()
{
	/*if (argc < 6)
	{
		std::cout << "Usage: rlEetTest ENGINE SCENEFILE KINEMATICSFILE EXPECTED_NUM_VERTICES_MAX EXPECTED_NUM_EDGES_MAX START1 ... STARTn GOAL1 ... GOALn" << std::endl;
		return EXIT_FAILURE;
	}*/

	
	std::string scene_path = "./CFP/CFP_Scene_1.xml";
	std::string kin_path = "./CFP/kuka-kr60-3.xml";
	
	//定义场景模型
	std::shared_ptr<rl::sg::Scene> scene;
	scene = std::make_shared<rl::sg::bullet::Scene>();

	//读入模型文件
	scene->load(scene_path);
	std::shared_ptr<rl::kin::Kinematics> kinematics(rl::kin::Kinematics::create(kin_path));
	std::vector<rl::math::Real>angles0{ 83.62, -57.27, 90.64, -11.48, -33.9, -80.43 };
	std::vector<rl::math::Real>angles1{ 35.43, -22.37, 60.43, 31.15, -42.46, -204.03 };

	Eigen::Matrix<rl::math::Unit, Eigen::Dynamic, 1> qUnits(kinematics->getDof());
	kinematics->getPositionUnits(qUnits);

	//起点转角
	rl::math::Vector start(kinematics->getDof());

	for (std::size_t i = 0; i < kinematics->getDof(); ++i)
	{
		start(i) = boost::lexical_cast<rl::math::Real>(angles0[i]);

		if (rl::math::UNIT_RADIAN == qUnits(i))
		{
			start(i) *= rl::math::DEG2RAD;
		}
		std::cout << start(i) << "  ";
	}
	std::cout << std::endl;
	//终点转角
	rl::math::Vector goal(kinematics->getDof());

	for (std::size_t i = 0; i < kinematics->getDof(); ++i)
	{
		goal(i) = boost::lexical_cast<rl::math::Real>(angles1[i]);

		if (rl::math::UNIT_RADIAN == qUnits(i))
		{
			goal(i) *= rl::math::DEG2RAD;
		}
		std::cout << goal(i) << "  ";
	}
	std::cout<<std::endl;

	rl::plan::DistanceModel model;
	model.kin = kinematics.get();
	model.model = scene->getModel(0);
	model.scene = scene.get();

	rl::plan::WorkspaceSphereExplorer explorer;
	rl::plan::Eet::ExplorerSetup explorerSetup;
	rl::plan::LinearNearestNeighbors nearestNeighbors(&model);
	rl::plan::Eet planner;
	rl::plan::UniformSampler sampler;

	rl::math::Vector3 explorerGoal;
	rl::math::Vector3 explorerStart;

	explorer.seed(0);
	planner.seed(0);
	sampler.seed(0);

	explorer.goal = &explorerGoal;
	explorer.greedy = rl::plan::WorkspaceSphereExplorer::GREEDY_SPACE;
	explorer.model = &model;
	explorer.radius = 0.025;
	explorer.range = 2.19;
	explorer.samples = 100;
	explorer.start = &explorerStart;

	explorerSetup.goalConfiguration = &goal;
	explorerSetup.goalFrame = -1;
	explorerSetup.startConfiguration = &start;
	explorerSetup.startFrame = -1;

	planner.alpha = 0.01f;
	planner.alternativeDistanceComputation = false;
	planner.beta = 0;
	planner.delta = 1.0f * rl::math::DEG2RAD;
	planner.distanceWeight = 0.1f;
	planner.epsilon = 1.0e-9f;
	planner.explorers.push_back(&explorer);
	planner.explorersSetup.push_back(explorerSetup);
	planner.gamma = 1.0f / 3.0f;
	planner.goal = &goal;
	planner.goalEpsilon = 0.1f;
	planner.goalEpsilonUseOrientation = false;
	planner.max.x() = 2.77634;
	planner.max.y() = 2.5;
	planner.max.z() = 3.5;
	planner.model = &model;
	planner.min.x() = -0.72466;
	planner.min.y() = -2.5;
	planner.min.z() = 0;
	planner.setNearestNeighbors(&nearestNeighbors, 0);
	planner.sampler = &sampler;
	planner.start = &start;

	sampler.model = &model;

	std::cout << "solve() ... " << std::endl;;
	std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
	bool solved = planner.solve();
	std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now();
	std::cout << "solve() " << (solved ? "true" : "false") << " " << std::chrono::duration_cast<std::chrono::duration<double>>(stopTime - startTime).count() * 1000 << " ms" << std::endl;

	std::cout << "NumVertices: " << planner.getNumVertices() << "  NumEdges: " << planner.getNumEdges() << std::endl;

	//读取路径
	rl::plan::VectorList paths = planner.getPath();
	rl::plan::VectorList::const_iterator i = paths.begin();
	std::cout << "paths.size() =" << paths.size() << std::endl;
	int t = 0;
	for (; i != paths.end(); ++i)
	{
		rl::math::Vector pos = *i * rl::math::RAD2DEG;
		std::cout << t <<":  "<< pos.transpose() << std::endl;
		t++;
	}


	if (solved)
	{
		/*if (boost::lexical_cast<std::size_t>(argv[4]) >= planner.getNumVertices() &&
			boost::lexical_cast<std::size_t>(argv[5]) >= planner.getNumEdges())*/
		if (120 >= planner.getNumVertices() &&
			100 >= planner.getNumEdges())
		{
			return EXIT_SUCCESS;
		}
		else
		{
			std::cerr << "NumVertices and NumEdges are more than expected for this test case.";
			return EXIT_FAILURE;
		}
	}

	return EXIT_FAILURE;

}

参考:
1.概率路线图(PRM)算法
2.RRT算法

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

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

相关文章

java计算机毕业设计ssm基金分析系统的设计与实现

项目介绍 计算机信息技术的发展,推动了基金信息化管理的进程,并随着互联网&#xff1a;概念的提出,各种互联网&#xff1a;软件也应运而生。在传统的管理中,各种信息管理难,传播速度慢,需要耗费很长时间统计核查,不能满足现代化的发展需求,基于JAVA的基金分析系统的提出解决了…

python+django家政服务中介网站系统

通常 一个Django model 对应一张数据表&#xff0c;model是以类的形式表现的 实现了ORM 对象与数据库映射 隐藏了数据访问细节 不需要写sql语句 admin是Django自带的 自动化数据管理界面 前端技术&#xff1a;nodejsvueelementui 我们最初的项目结构由五个文件组成&#xf…

《树莓派项目实战》第六节 使用超声波模块测距

目录 6.1 引脚介绍 6.2 工作原理 6.3 使用注意 6.4 连接到树莓派 6.5 编写代码输出距离 在本节&#xff0c;我们将学习如何使用HC-SR04超声波模块测量前方障碍物的距离&#xff0c;该项目设计到的材料有&#xff1a; 树莓派 * 1面包板 * 1杜邦线若干HC-SR04超声波模块* 1…

Mysql进阶之索引与视图和三大范式

1、索引&#xff08;index&#xff09; 1.1、什么是索引&#xff1f; 索引是在数据库表的字段上添加的&#xff0c;是为了提高查询效率存在的一种机制。 一张表的一个字段可以添加一个索引&#xff0c;当然&#xff0c;多个字段联合起来也可以添加索引。 索引相当于一本书的…

redis搭建主从、redis搭建集群、redis中StrictRedis()、RedisCluster()方法与python交互

一、StrictRedis方法 创建对象&#xff0c;指定host、port、db与指定的服务器端口连接&#xff0c;其中默认host为localhost、port为6379、db为0&#xff0c;不同类型调用的实例方法不同&#xff0c;与redis命令一致&#xff0c;方法需要的参数与命令参数一致 from redis imp…

Xavier(8):Xavier使用速腾聚创激光雷达运行a-loam算法部分报错与解决方案

文章目录1 速腾聚创激光雷达驱动报错&#xff1a;Project cv_bridge specifies /usr/include/opencv as an include dirProject grid_map_cv specifies /usr/include/opencv as an include dir2 a-loam算法报错&#xff1a; fatal error: opencv/cv.h: 没有那个文件或目录报错&…

无线社工基础

无线社工基础 一些社会工程学密码生成器&#xff08;百度&#xff09; Crunch工具 /usr/share/crunch/charset.lstcrunch 最短长度 最长长度 字符集 选项crunch 8 12 -f /usr/share/crunch/charset.lst ualpha -o /root/pass.txt Windows下对附近无线网络进行扫描 WirelessMon…

MySQL事务/事务与数据库底层数据/多点回滚/隔离级别/悲观锁和乐观锁/锁模式和分类/相关锁总结/JDBC事务实现

文章目录MySQL事务概述事务事务与数据库底层数据事务控制语句事务处理基本测试多点回滚相关日志问题redo logundo log隔离级别常见问题事务隔离性隔离级别的范围总结隔离等级读未提交读已提交可重复读串行化并发写问题幻读问题悲观锁和乐观锁锁模式锁分类按加锁方式分类按照算法…

基于贝叶斯推理估计稳态 (ST) 和非稳态 (NS) LPIII 模型分布拟合到峰值放电(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

ShardingSphere笔记(一): 经验和踩坑总结

ShardingSphere笔记&#xff08;一&#xff09;&#xff1a; 使用经验总结 文章目录ShardingSphere笔记&#xff08;一&#xff09;&#xff1a; 使用经验总结一、背景框架选择二、ShardingSphere-jdbc 只是一个帮助你路由的框架&#xff08;踩坑总结&#xff09;1. 它默认会认…

支持末尾携带标签的多行TextView

项目开发过程中&#xff0c;遇到个UI上的需求&#xff0c;本着不重复造轮子、敏捷开发的原则&#xff0c;于是乎网上找寻了一番&#xff0c;发现还是自己搞吧&#xff0c;搜不到这样的需求&#xff0c;先看下我们的效果。 总结有以下三点需要注意&#xff1a; 末尾vip部分是…

$19服务:DTCStatusMask和statusofDTC bit 定义

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;当ECU产生DTC时&#xff0c;我们只知道有故障发生了&#xff0c;并不清楚该故障什么时候发生&#xff0c;现在是否已经恢复、发生过几次&#xff0c;恢复过几次等信息&#xff0c;基于此ISO发布的14229-1使用DTC状态…

[附源码]SSM计算机毕业设计志愿者管理系统论文2022JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【VTK+有限元后处理】可视化结果云图

构建vtkUnstructuredGrid对象 为了读取不同格式的有限元计算结果文件&#xff0c;我们先写一个FEDataModel类来管理有限元的几何拓扑和属性信息。 class FEDataModel:"""有限元数据模型类"""def __init__(self):self.nodes [] # 节点几何坐标…

斐波那契数列和斐波那契数

一、什么是斐波那契数列 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;因数学家莱昂纳多斐波那契&#xff08;Leonardo Fibonacci&#xff09;以兔子繁殖为例子而引入&#xff0c;故又称为“兔子数列”&#xff0c;指的是这…

【考研复试】计算机专业考研复试英语常见问题三(个人选择/学业规划篇)

相关链接&#xff1a; 【考研复试】计算机专业考研复试英语常见问题一&#xff08;家庭/家乡/学校篇&#xff09;【考研复试】计算机专业考研复试英语常见问题二&#xff08;研究方向/前沿技术/本科毕设篇&#xff09;【考研复试】计算机专业考研复试英语常见问题三&#xff0…

C++---哈希

目录 1. unordered系列关联式容器 1.1 unordered_map 1.1.1 unordered_map的介绍 1.1.2 unordered_map的接口说明 1.2 unordered_set 2. 底层结构 2.1 哈希概念 2.2 哈希冲突 2.3 哈希函数 2.4 哈希冲突解决 2.4.1 闭散列 2.4.2 开散列 3. 封装unorder_map和unord…

MySQL增删改查进阶 — 表的设计

文章目录表的设计1.设计思路2.实体固定关系的套路2.1 一对一关系2.2 一对多关系2.3 多对多关系3.总结表的设计 表的设计实际上要做的工作就是明确一个程序里&#xff0c;需要使用几个数据库&#xff0c;几个表&#xff0c;表里都有哪些列。 1.设计思路 先明确实体再明确实体…

6. Design A Web Crawler

title: Notes of System Design No.10 — Design a Web Crawler description: ‘Design a Web Crawler’ date: 2022-05-13 18:01:58 tags: 系统设计 categories: 系统设计 00. What is Web Crawler? Q &#xff1a;uh just for now lets just do html pagesbut your web cr…

Explaining Deepfake Detection by Analysing Image Matching 翻译

点击查看对应的代码 摘要 本文旨在解释深度伪造检测模型在仅由二进制标签做有监督时如何学习图像的伪迹特征。为此&#xff0c;从图像匹配的角度提出如下三个假设。1、深度伪造检测模型表明基于视觉概念的真/假图片既不与源图片相关也不与目标图片相关而是与伪迹图片相关。2、…