Qt框架下的人工势场法仿真实现
基于Qt框架的人工势场法仿真实现,包含完整的界面和算法核心。
一、项目结构与核心类设计
1. 主程序结构
ArtificialPotentialField/
├── main.cpp # 程序入口
├── mainwindow.h/cpp # 主窗口类
├── potentialfield.h/cpp # 势场计算核心类
├── robot.h/cpp # 机器人类
├── obstacle.h/cpp # 障碍物类
├── simulationwidget.h/cpp # 仿真显示部件
└── controlpanel.h/cpp # 控制面板
2. 核心类定义
potentialfield.h – 势场计算核心
#ifndef POTENTIALFIELD_H
#define POTENTIALFIELD_H
#include <QObject>
#include <QPointF>
#include <QVector>
#include <QDebug>
class PotentialField : public QObject
{
Q_OBJECT
public:
struct Parameters {
double k_att = 1.0; // 引力系数
double k_rep = 1000.0; // 斥力系数
double d0 = 50.0; // 障碍物影响距离
double step_size = 5.0; // 步长
double goal_tolerance = 5.0; // 目标容差
int max_iterations = 1000; // 最大迭代次数
};
explicit PotentialField(QObject *parent = nullptr);
// 设置参数
void setParameters(const Parameters ¶ms);
// 路径规划
QVector<QPointF> planPath(const QPointF &start,
const QPointF &goal,
const QVector<QPointF> &obstacles);
// 计算单个点的合力
QPointF calculateTotalForce(const QPointF &position,
const QPointF &goal,
const QVector<QPointF> &obstacles);
signals:
void calculationProgress(int percent);
void pathCalculated(const QVector<QPointF> &path);
void algorithmFinished(bool success, const QString &message);
private:
// 计算引力
QPointF calculateAttractiveForce(const QPointF &position,
const QPointF &goal);
// 计算斥力
QPointF calculateRepulsiveForce(const QPointF &position,
const QPointF &obstacle);
// 计算两点距离
double distance(const QPointF &p1, const QPointF &p2);
Parameters m_params;
};
#endif
potentialfield.cpp – 势场算法实现
#include "potentialfield.h"
#include <cmath>
PotentialField::PotentialField(QObject *parent)
: QObject(parent) {}
void PotentialField::setParameters(const Parameters ¶ms) {
m_params = params;
}
QVector<QPointF> PotentialField::planPath(const QPointF &start,
const QPointF &goal,
const QVector<QPointF> &obstacles) {
QVector<QPointF> path;
QPointF current = start;
int iteration = 0;
path.append(current);
while (iteration < m_params.max_iterations) {
// 检查是否到达目标
if (distance(current, goal) < m_params.goal_tolerance) {
path.append(goal);
emit algorithmFinished(true, "成功到达目标点!");
emit calculationProgress(100);
return path;
}
// 计算合力
QPointF totalForce = calculateTotalForce(current, goal, obstacles);
// 归一化并移动
double forceMagnitude = std::sqrt(totalForce.x() * totalForce.x() +
totalForce.y() * totalForce.y());
if (forceMagnitude > 0) {
QPointF direction(totalForce.x() / forceMagnitude,
totalForce.y() / forceMagnitude);
current += direction * m_params.step_size;
} else {
// 合力为零,可能陷入局部最小值
emit algorithmFinished(false, "陷入局部最小值");
return path;
}
path.append(current);
iteration++;
// 更新进度
int progress = (iteration * 100) / m_params.max_iterations;
emit calculationProgress(progress);
}
emit algorithmFinished(false, "达到最大迭代次数");
return path;
}
QPointF PotentialField::calculateTotalForce(const QPointF &position,
const QPointF &goal,
const QVector<QPointF> &obstacles) {
QPointF attractiveForce = calculateAttractiveForce(position, goal);
QPointF repulsiveForce(0, 0);
for (const QPointF &obstacle : obstacles) {
QPointF repForce = calculateRepulsiveForce(position, obstacle);
repulsiveForce += repForce;
}
return attractiveForce + repulsiveForce;
}
QPointF PotentialField::calculateAttractiveForce(const QPointF &position,
const QPointF &goal) {
double dist = distance(position, goal);
QPointF direction = goal - position;
if (dist > 0) {
direction /= dist;
}
return direction * m_params.k_att * dist;
}
QPointF PotentialField::calculateRepulsiveForce(const QPointF &position,
const QPointF &obstacle) {
double dist = distance(position, obstacle);
if (dist > m_params.d0 || dist == 0) {
return QPointF(0, 0);
}
QPointF direction = position - obstacle;
if (dist > 0) {
direction /= dist;
}
double forceMagnitude = m_params.k_rep * (1.0/dist - 1.0/m_params.d0) * (1.0/(dist*dist));
return direction * forceMagnitude;
}
double PotentialField::distance(const QPointF &p1, const QPointF &p2) {
return std::sqrt((p1.x() - p2.x()) * (p1.x() - p2.x()) +
(p1.y() - p2.y()) * (p1.y() - p2.y()));
}
simulationwidget.h – 仿真显示部件
#ifndef SIMULATIONWIDGET_H
#define SIMULATIONWIDGET_H
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>
#include <QPointF>
#include <QVector>
class SimulationWidget : public QWidget
{
Q_OBJECT
public:
enum EditMode {
MODE_START,
MODE_GOAL,
MODE_OBSTACLE
};
explicit SimulationWidget(QWidget *parent = nullptr);
// 设置模式
void setEditMode(EditMode mode);
// 设置路径
void setPath(const QVector<QPointF> &path);
// 设置障碍物
void setObstacles(const QVector<QPointF> &obstacles);
// 获取起点、终点、障碍物
QPointF getStartPoint() const { return m_startPoint; }
QPointF getGoalPoint() const { return m_goalPoint; }
QVector<QPointF> getObstacles() const { return m_obstacles; }
// 清空
void clearAll();
void clearPath();
signals:
void environmentChanged();
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
private:
void drawRobot(QPainter &painter, const QPointF &position);
void drawGoal(QPainter &painter, const QPointF &position);
void drawObstacle(QPainter &painter, const QPointF &position);
void drawPath(QPainter &painter, const QVector<QPointF> &path);
EditMode m_editMode;
QPointF m_startPoint;
QPointF m_goalPoint;
QVector<QPointF> m_obstacles;
QVector<QPointF> m_path;
bool m_showPotentialField;
const int ROBOT_RADIUS = 10;
const int GOAL_RADIUS = 15;
const int OBSTACLE_RADIUS = 20;
};
#endif
mainwindow.h – 主窗口
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include "potentialfield.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
// 工作线程类
class PathPlanningThread : public QThread
{
Q_OBJECT
public:
PathPlanningThread(PotentialField *potentialField,
const QPointF &start,
const QPointF &goal,
const QVector<QPointF> &obstacles,
QObject *parent = nullptr)
: QThread(parent), m_potentialField(potentialField),
m_start(start), m_goal(goal), m_obstacles(obstacles) {}
protected:
void run() override {
QVector<QPointF> path = m_potentialField->planPath(m_start, m_goal, m_obstacles);
emit pathReady(path);
}
signals:
void pathReady(const QVector<QPointF> &path);
private:
PotentialField *m_potentialField;
QPointF m_start;
QPointF m_goal;
QVector<QPointF> m_obstacles;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onStartClicked();
void onPauseClicked();
void onResetClicked();
void onPathCalculated(const QVector<QPointF> &path);
void onCalculationProgress(int percent);
void onAlgorithmFinished(bool success, const QString &message);
// 编辑模式切换
void onSetStartMode();
void onSetGoalMode();
void onSetObstacleMode();
private:
Ui::MainWindow *ui;
PotentialField *m_potentialField;
PathPlanningThread *m_planningThread;
bool m_isRunning;
};
#endif
3. UI界面设计 (mainwindow.ui)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>800</height>
</rect>
</property>
<property name="windowTitle">
<string>人工势场法路径规划仿真</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="SimulationWidget" name="simulationWidget">
<property name="minimumSize">
<size>
<width>800</width>
<height>600</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="controlGroupBox">
<property name="title">
<string>控制面板</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<!-- 参数设置 -->
<widget class="QGroupBox" name="parametersGroupBox">
<property name="title">
<string>势场参数</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>引力系数:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="kAttSpinBox">
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<!-- 类似添加其他参数:斥力系数、影响距离、步长等 -->
</layout>
</widget>
<!-- 编辑模式 -->
<widget class="QGroupBox" name="editModeGroupBox">
<property name="title">
<string>编辑模式</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="setStartButton">
<property name="text">
<string>设置起点</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="setGoalButton">
<property name="text">
<string>设置终点</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="setObstacleButton">
<property name="text">
<string>添加障碍物</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearAllButton">
<property name="text">
<string>清空所有</string>
</property>
</widget>
</item>
</layout>
</widget>
<!-- 控制按钮 -->
<widget class="QGroupBox" name="controlButtonsGroupBox">
<property name="title">
<string>仿真控制</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="startButton">
<property name="text">
<string>开始规划</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pauseButton">
<property name="text">
<string>暂停</string>
</property>
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="text">
<string>重置</string>
</property>
</widget>
</item>
</layout>
</widget>
<!-- 进度条 -->
<widget class="QGroupBox" name="progressGroupBox">
<property name="title">
<string>进度</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>就绪</string>
</property>
</widget>
</item>
</layout>
</widget>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>SimulationWidget</class>
<extends>QWidget</extends>
<header>simulationwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
4. 主程序入口
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
二、编译配置 (CMakeLists.txt)
cmake_minimum_required(VERSION 3.16)
project(ArtificialPotentialField)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
# 添加可执行文件
add_executable(${PROJECT_NAME}
main.cpp
mainwindow.cpp
mainwindow.ui
potentialfield.cpp
simulationwidget.cpp
)
# 链接Qt库
target_link_libraries(${PROJECT_NAME}
PRIVATE Qt6::Core Qt6::Widgets
)
三、仿真功能说明
1. 核心算法特点
- 引力场计算:与目标点距离成正比
- 斥力场计算:与障碍物距离成反比,设置影响阈值
- 合力计算:矢量叠加引力与斥力
- 局部最小值处理:检测并提示局部最小值问题
2. 可视化功能
- 起点(绿色机器人图标)
- 终点(红色目标图标)
- 障碍物(蓝色圆形)
- 规划路径(绿色线条)
- 实时力场显示(可选)
3. 交互功能
- 鼠标点击设置起点、终点、障碍物
- 实时参数调整
- 动画演示机器人运动
- 规划进度显示
参考代码 仿真人工势场法的验证采用C++上的qt框架 www.youwenfan.com/contentcsv/71768.html
四、使用说明
-
环境设置:
- 点击"设置起点"按钮,在仿真区域点击设置机器人起点
- 点击"设置终点"按钮,设置目标点
- 点击"添加障碍物"按钮,添加障碍物
-
参数调整:
- 引力系数:控制目标点的吸引力
- 斥力系数:控制障碍物的排斥力
- 影响距离:障碍物斥力的作用范围
- 步长:机器人每一步的移动距离
-
运行仿真:
- 点击"开始规划"启动路径规划
- 实时观察机器人的运动轨迹
- 可暂停、重置仿真
五、扩展功能建议
-
势场可视化:
- 添加网格显示势场强度
- 用颜色渐变表示势能大小
- 绘制力场矢量图
-
算法改进:
- 添加虚拟目标点避免局部最小值
- 实现动态窗口法结合
- 添加速度势场支持动态障碍物
-
数据记录:
- 保存规划路径数据
- 记录势场参数
- 导出仿真结果图片