数据结构——校园导游系统

2023-05-16

校园导游系统

1. 要求

大二下学期修了数据结构这门课,课设的要求是做一个校园导航系统。详细的要求如下

问题描述:

当我们参观校园时,会遇到如下问题:从当前所处位置去校园另外一个位置,要走什么样的路线距离最短?本课程设计任务要求,在给出校园各主要建筑的名称信息及有路线连通的建筑之间的距离的基础上,利用校园导游系统计算出给定起点到终点之间距离最近的行进路线。

任务要求:

(1)从地图文件中读取校园主要建筑信息及建筑之间的距离信息。
(2)计算出给定的起点到终点之间距离最近的行进路线。
(3)输出该路线及其总距离。

2. 分析

2.1 数据提取

第一个问题,如何看地图把信息提取出来,根据目前所掌握的知识来看并没有很智能、自动化的方法,只有人手工地把地图重要信息进行提取并录入。这些信息包括各个地点与各地点之间的距离。接下来就以我的学校地图为例。

为了演示的方便,便于后期算法正确与否的验证,提取中其中15个重要的点

同样为了演示、检验的方便,目测(编造)点与点之间的距离以及关系,来抽象成一张图:

而数据结构与算法这门课可以帮助解决的问题是怎么来存储这些数据。这个问题比较好回答,地图抽象出来就是图结构,而图的信息的常常用邻接矩阵来存储。

2.2 路径搜索

第二个问题,如何得到最优路径,以我的水平,或者说我接触过、学过的方法来说,可以选择的有两种算法,Dijkstra算法和Floyd算法。

3 设计

3.1 功能设计

首先,要先确定这个系统能实现什么样的功能。

3.1.1 菜单

第一肯定是要有一个菜单,当然,这是一个课设就不搞什么UI了,就是最基础的输入选项选择式菜单就可以了。

3.1.2 点位选择

第二,要找出两点最短路径,那肯定需要能够进行起点,终点的选择

3.1.3 地图数据来源选择

第三,我希望我做出来的东西不只是一个只是用来演示的东西,所以地图的数据肯定不能写死,希望能够实现只要能把地图数据抽取出来抽象成图,无论多大多小,都可以用我这个工具来找最短路径。因此,这就需要这个系统可以进行地图数据来源的选择。可以是默认的,也可以是自己手动输入的图数据。并且地图起点、中断选择界面的内容可以根据我们的数据来源进行对应的改变。

如果想要自定义输入地图数据,那么肯定需要有一个固定的输入格式,这样才能通过程序来把输入的数据提取出来,所以我们要规定一个格式。我的规定是,需要把每个点之间的关系都告诉程序,因此就需要很多组的两个端点与端点距离。因此我规定

每组三个数据,组内数据以空格分开,组间以分号分开。例如节点1序号 节点2序号 权值;节点3序号 节点4序号 权值;...

这个自定义的问题解决的,但是随后又想到,如果这个图是一个很大的图,我们自己手动输入会很麻烦,但是这是必须的。可是最要救命的是,如果下一次还想接着用,那么就要再输入一大堆,非常的麻烦,这是可以避免的。因此可以设计一个配置文件的方法方式,通过直接读取配置文件的内容来直接配置好地图数据。这样的话我们也需要再对配置文件的内容格式进行一个规范,我很简单地使用了这样的标准

地图数据在前 地图节点名称在后,写在同一行,之间用星号(*)隔开,最终以感叹号(!)结尾
地图数据:每组三个数据,组内数据以空格分开,组间以分号分开
例如:节点1序号 节点2序号 权值;节点3序号 节点4序号 权值;...
节点名称:顺序填写逗号分割.
例如:北门,东门,南门
例如:1 2 30; 2 3 50;*北门,东门,西门!
注意:为了避免中文乱码的问题,配置文件的编码格式需要是ANSI

3.1.4 算法选择

第四,能够使用不同的算法来处理。现在我还是个小菜鸡,只会两种算法Dijkstra算法和Floyd算法,而且这两个算法最后的结果看不出来差别。

3.2 代码结构设计

我对于程序的代码结构没什么研究,并且没有写出过结构优美的代码,那是我很渴望的。我希望自己写出来的程序是很清晰明了的,代码之间的关系、结构都很清晰,我也在慢慢地摸索。而我所知道或者所秉承的观念是很朴素的:1.不同功能的代码块要尽量解耦 2. 变量尽可能地用面向对象的思维来进行管理,在C语言里具体的体现就是尽可能用结构体将变量进行整合。

因为在RM战队写过屎山代码,所以我脑子里有一个很浅显或者说是粗俗的观点:各个部分要有一个Controller结构体来进行管理。因此怀着这样的想法,我把代码在结构上整体分成了三部分:

  • 算法模块
  • 系统管理模块
  • 界面展示模块

然后整个系统围绕着三个控制器(结构体)展开:

  • 系统控制器 SystemController
  • 算法控制器 DijkstraController、FloydController
  • 图信息控制器 MGraph

在控制器中SystemController掌控着整个系统的所有信息,如:地图数据、配置文件数据、算法控制器、选项结果等等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GWOBNuq8-1671969502895)(https://www.houenup.com/wp-content/uploads/2022/12/数据结构课设程序控制器.png)]

4 具体实现

4.1 程序流程

整个程序的入口是main函数,在main函数中调用System_Start()进入系统,System_Start的作用就是将系统中核心的控制器(控制结构体)之间建立联系并初始化相关数据,然后进入界面的控制。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UARpp9Z8-1671969502895)(https://www.houenup.com/wp-content/uploads/2022/12/数据结构课设程序流程图1.png)]

4.2 算法实现

4.2.1 Dijkstra算法:

​ Dijkstra算法的核心是对于三个列表的更新迭代,三个列表分别为,前驱节点列表、顶点最短路径列表、顶点是否确认列表。

具体的算法实现步骤为:

  1. 初始化:从出发点开始,将出发点确定为第一个已经确定最短路径的顶点,最短路径为0,然后更新与出发点有路径的顶点的最短路径和对应顶点的前驱顶点。
  2. 进行循环更新:从未确定最短路径的顶点中选取最短路径最小的顶点为新确定的顶点,然后更新与新确认顶点有路径的顶点的最短路径和前驱顶点,如果新路径更长就不更新,更短就更新。
算法控制器
/** Dijkstra算法控制器 **/
typedef struct
{
    int forerunner_list[MAX_VERTICES];        /** 前驱顶点列表 **/
    int confirmed_flag_list[MAX_VERTICES];    /** 该顶点是否确定了最短路径 **/
    int distance_list[MAX_VERTICES];          /** 最短路径距离 **/
    int start_point;                          /** 起点 **/
    int end_point;                            /** 终点 **/
    int latest_confirmed_node;                /** 最新被确认的节点 **/
    int route[MAX_VERTICES];                  /** 最终算出来的最优路径 **/
    int sum_distance;                         /** 路径总距离 **/
    MGraph * mgraph;                          /** 存储了图数据的指针 **/
}DijkstraControler;
初始化函数
/**
 * @brief 算法初始化
 * @param controler:算法控制器
 * @param system_data: 系统控制器
 * */
void Dijkstra_Init(DijkstraControler* controler,SystemControler* system_data)
{
    /**
     * 初始化:1.出发点是第一的已经确定最短路径的顶点
     *       2.更新与出发点有路径的顶点的最短路径和前驱顶点
     * */
     int distance_temp = 0;
     /* 获取需要使用的信息 */
    controler->mgraph = system_data->graph_data;
    controler->start_point = system_data->start_point;
    controler->end_point = system_data->end_point;
    for (int i = 0; i < MAX_VERTICES; ++i) {
        controler->distance_list[i] = 0;
        controler->confirmed_flag_list[i] = 0;
        controler->forerunner_list[i] = 0;
    }
    int node_num =  controler->mgraph->numNodes;
    /*顶点最短路径初始化为无穷*/
    for (int i = 0; i < node_num; ++i) {
        controler->distance_list[i+1] = GIGANTIC;         //0弃除不用 从1开始

    }

    /* 出发点是第一的已经确定最短路径的顶点,自己到自己的距离是0 */
    controler->latest_confirmed_node = controler->start_point;
    controler->distance_list[controler->start_point] = 0;
    controler->confirmed_flag_list[controler->start_point] = 1;     //起始点已经确定
    controler->forerunner_list[controler->start_point] = controler->start_point;
    /* 更新与出发点有路径的顶点的最短路径和前驱顶点 */
    Update_Distance_Predecessor(controler,controler->start_point);

}
更新确认节点函数
/**
 * @brief  更新确认节点节点
 * */
void Update_Confirmed_Node(DijkstraControler* controler)
{
     int min_distance_index;
    for (int i = 0; i < controler->mgraph->numNodes; ++i)       //找最短路径最小的顶点
    {
        if (controler->confirmed_flag_list[i+1] != 1)
        {
            min_distance_index  = Find_Min(controler->distance_list[i+1],i+1);
            controler->latest_confirmed_node = min_distance_index;             //更新最新被确认的顶点
        }
    }
    min_distance = GIGANTIC;
    controler->confirmed_flag_list[controler->latest_confirmed_node] = 1;
}
更新最短路径与前驱节点函数
/**
 * @brief 更新与确认顶点有路径的顶点的最短路径
 * @param controler:控制器
 * @param comfirmed_point: 需要更新的与其之间有路径的点
 * */
 void Update_Distance_Predecessor(DijkstraControler* controler,int comfirmed_point)
{
     int node_num = controler->mgraph->numNodes;
     int distance_temp = 0;
    for (int i = 0; i < node_num; ++i)
    {
        distance_temp = controler->mgraph->arc[comfirmed_point][i+1];

        if ( distance_temp != 0)  //找到有路径的顶点的路径长度
        {
            //如果与confirmed_point有关的路径的最短距离比它原来的最短距离更小 就更新它
            if (distance_temp + controler->distance_list[comfirmed_point] < controler->distance_list[i+1] || controler->distance_list[i+1]==GIGANTIC)
            {
                controler->distance_list[i+1] =  distance_temp + controler->distance_list[comfirmed_point]; //更新最短距离
                Update_predecessor_node(controler,comfirmed_point,i+1);//更新前驱顶点
            }

        }


    }
}
算法循环函数
/**
 * @brief 算法循环
 * @param controler:算法控制器
 * */
 void Dijkstra_Loop(DijkstraControler* controler)
{
     /**
      * 循环:1.从未确定最短路径的顶点中选取最短路径最小的顶点为新确定最短路径的顶点;
      *      2.更新与新确认顶点有路径的顶点的最短路径和前驱顶点。(如果新路径更短就更新,更长则不更新)
      *
      * 结束条件:所有的点都被确定过最短路径了即寻找完毕
      * */

     int min_distance = 0;
     int new_point = 0;
    while (!controler->confirmed_flag_list[controler->end_point])
    {
        /*从未确定最短路径的顶点中选取最短路径最小的顶点为新确定最短路径的顶点*/
        Update_Confirmed_Node(controler);

        /* 更新与新确认顶点有路径的顶点的最短路径和前驱顶点 */
        Update_Distance_Predecessor(controler,controler->latest_confirmed_node);

    }


}

4.2.2 Floyd算法

Floyd算法的核心在于对两张表的更新迭代,分别为两点距离表和两点间中继节点表。

具体的算法实现步骤为:

  1. 初始化两张表,将中继节点表初始化为全-1,将两点距离表中有直接路径的点值初始化为其距离,无直接路径的初始化为-1
  2. 进行更新循环,不断地加入中继节点,遍历整张距离表,如果直接距离大于中继之后的间接距离,则更新两点之间距离,并将对应的中继节点表中的位置更新为当下的中继节点。
Floyd算法控制器
/** Floyd算法控制器 **/
typedef struct
{
    int shortest_distance_table[MAX_VERTICES][MAX_VERTICES];    /** 两点之间最短距离表 (0,0)弃用 **/
    int relay_node_table[MAX_VERTICES][MAX_VERTICES];           /** 两点之间中继节点表 (0,0)弃用 **/
    int start_point;                                            /** 起点 **/
    int end_point;                                              /** 终点 **/
    int route[MAX_VERTICES];                                    /** 最终算出来的最优路径 **/
    int route_num;                                              /** 路线途径点个数 **/
    int sum_distance;                                           /** 路径总距离 **/
    MGraph * mgraph;                                            /** 存储了图数据的指针 **/
}FloydControler;
初始化函数
/**
 * @brief 算法初始化函数
 * */
void Floyd_Init(FloydControler* controler, SystemControler* system_data)
{
    controler->mgraph = system_data->graph_data;
    controler->start_point = system_data->start_point;
    controler->end_point = system_data->end_point;
    controler->route[0] = controler->start_point;
    controler->route_num = 1;
    /* 初始化两张表 */
    for (int i = 0; i < MAX_VERTICES-1; ++i) {
        for (int j = 0; j < MAX_VERTICES-1; ++j) {

            controler->relay_node_table[i+1][j+1] = GIGANTIC;
            if (controler->mgraph->arc[i+1][j+1] == 0)
            {
                if (i == j)
                {
                    controler->shortest_distance_table[i+1][j+1] = 0;
                } else
                {
                    controler->shortest_distance_table[i+1][j+1] = GIGANTIC;
                }

            }
            else
            {
                controler->shortest_distance_table[i+1][j+1] = controler->mgraph->arc[i+1][j+1];
            }
        }

    }
}
更新循环函数
/**
 * @brief 更新循环
 * */
 void Floyd_Upgrade_Loop(FloydControler* controler)
{
     int node_num = controler->mgraph->numNodes;
     //最短距离表更新
    for (int v = 1; v < node_num; ++v) {
        for (int i = 1; i < node_num+1; ++i) {
            for (int j = 1; j < node_num+1; ++j) {
                //中继节点到某一点不通就不更新
                if (controler->shortest_distance_table[i][v]==GIGANTIC || controler->shortest_distance_table[v][j] == GIGANTIC)
                {
                    ;
                }
                else
                {   //中继到两边都通,总长更小就更新
                    if ((controler->shortest_distance_table[i][j] == GIGANTIC) || (controler->shortest_distance_table[i][j]>controler->shortest_distance_table[i][v]+controler->shortest_distance_table[v][j]))
                    {
                        controler->shortest_distance_table[i][j] = controler->shortest_distance_table[i][v]+controler->shortest_distance_table[v][j];
                        controler->relay_node_table[i][j] = v;      //更新中继节点表
                    }
                }

            }
        }

    }

}
路径寻找函数
/**
 * @brief 寻找最短路径
 * */

void Floyd_Find_Path(FloydControler* controler,int start,int end)
{
    if (controler->relay_node_table[start][end] == GIGANTIC)
    {
        controler->route[controler->route_num++] = end;
        controler->sum_distance+=controler->shortest_distance_table[start][end];
    } else
    {
        int mid = controler->relay_node_table[start][end];
        Floyd_Find_Path(controler,start,mid);
        Floyd_Find_Path(controler,mid,end);
    }
}

4.3 管理实现

总体来说整个系统管理起来的逻辑是比较简单的,就是通过用户在对应界面选择的选项来调用不同的函数实现界面的跳转、数据的选择与输出等。

其中相对花了一些时间的地方在输入数据的提取、配置文件的数据提取上。

主菜单管理

/**
 * @brief 主菜单管理
 * */

void MainMenu_Control(SystemControler* system_controler)
{
    uint8_t temp = 0;
    static int time_flag = 0;  //用来表示是首次进入该界面还是成功设置完成后再次进入的 0 首次 1 成功设置  2 设置错误

    system("cls");  		//刷新界面
    MainMenu_Show();
    switch (time_flag) {
        case 0:
            printf("请输入选项:");
            scanf("%s",&temp);
            break;
        case 2:
            printf("请选择正确选项!\r\n");
            printf("请输入选项:");
            scanf("%s",&temp);
            break;
    }


    if ((temp-48 <1 || temp-48>6) && temp != '*')
    {
        time_flag = 2;
        MainMenu_Control(system_controler);
    }
    switch (temp-48) {
        case 1:
            MapDataSelectionMenu_Control(system_controler);
            break;
        case 2:
            Origin_LocationSelectionMenu_Control(system_controler);
            break;
        case 3:
            Destination_LocationSelectionMenu_Control(system_controler);
            break;
        case 4:
            AlgorithmSelectionMenu_Control(system_controler);
            break;
        case 5:
            RouteOutputMenu_Control(system_controler);
            break;
        case 6:
            Configuration_Import_Control(system_controler);
            break;
        case -6:
            Thanks_Show();
            break;

    }
}

可以看到这个switch就是选项的逻辑,选择不同的选项会调用不同的菜单管理控制函数,每一个菜单管理控制函数都包括菜单的展示、逻辑的实现… 基本上方法是一样的。

导游系统执行函数

系统,或者说是算法,的执行是在最后用户选择输出地址的时候才开始执行的。如果使用的是默认的地图数据,那么地图数据的提取也是在这个时候开始的。

/**
 * @brief 导游系统执行函数
 * */
void Work_Start(SystemControler* system_controler)
{
    Information_Entry(system_controler);
    /*使用Dijkstra算法*/
    if (system_controler->algorithm_options == 0)
    {
        Dijkstra_Init(system_controler->pdijkstra_controler,system_controler);
        Dijkstra_Loop(system_controler->pdijkstra_controler);
        Dijkstra_Find_Path(system_controler->pdijkstra_controler);

    }
    /*使用Floyd算法*/
    else
    {
        Floyd_Init(system_controler->pfloyd_controler,system_controler);
        Floyd_Upgrade_Loop(system_controler->pfloyd_controler);
        Floyd_Find_Path(system_controler->pfloyd_controler,system_controler->pfloyd_controler->start_point,system_controler->pfloyd_controler->end_point);

    }
}

地图数据录入函数

该函数的所有精髓都在数据数据提取函数中。

/**
 * @brief 系统景点信息录入函数
 * @param data:存放景点数据的邻接矩阵指针
 * */
void Information_Entry(SystemControler* system_controler)
{
    if (system_controler->map_data_options)  //使用自定义地图数据
    {

        Data_Extracte(data_buffer,system_controler->data);  //将原始数据提取出来并转换成整数类型存储在extracted_data中

        /*邻接矩阵初始化*/
        Adjacency_Matrix_Init(system_controler->graph_data,system_controler->data);
    }
    else
    {
        Adjacency_Matrix_Init(system_controler->graph_data,system_controler->data);
    }


}
数据数据提取函数

对于有固定格式的数据的提取,我一般都是用状态机编程的思想来实现。而这个实现的方式是从一个开源的RM裁判系统解析程序中学习到的。当然这样的方法可能不是最好的,但是易懂、易实现。这个方法的总体思路是,把解析的过程分成若干步,然后用枚举来定义这个连续的步骤,通过枚举变量的数值来得知到了哪一步,下一步该到哪个状态。

/**
 * @brief 输入数据提取操作
 * @param data_input:需要提取数据的存储地址
 * @param data_output:提取数据输出地址
 * */
void Data_Extracte(char* data_input,int* data_output)
{
    int run_flag = 1;
    int index_buffer = 0;       //提取暂存buffer索引
    int index_raw_data = 0;     //原始数据扫描索引
    int index_data = 0;         //提取完成数据索引
    char buffer[20];            //用来暂存提取的时候读到的数据
    while (run_flag)
    {
        switch (step)
        {
            case NODE_1:
            {
                if(data_input[index_raw_data] == ' ')
                {
                    step++;
                }
                else
                {
                    buffer[index_buffer] = data_input[index_raw_data];
                    index_raw_data++;
                    index_buffer++;
                }
            }break;
            case BLANK_1:       /*遇见空格就把上一次的字符数据给转换成数字数据存在存放提取数据的数组中*/
            {
                    buffer[index_buffer] = '\0';            /*将数组构造成字符串形式*/
                    data_output[index_data] = atoi(buffer);   //将提取出来的数据转换成int型存放在处理好的数组中
                    memset(buffer,0,sizeof(buffer)/sizeof(char));
                    step++;
                    index_raw_data++;
                    index_buffer++;
                    index_buffer = 0;               //重置buffer索引
                    index_data++;
            }break;
            case NODE_2:
            {
                if(data_input[index_raw_data] == ' ')
                {
                    step++;

                }
                else
                {
                    buffer[index_buffer] = data_input[index_raw_data];
                    index_raw_data++;
                    index_buffer++;
                }
            }break;
            case BLANK_2:
            {
                buffer[index_buffer] = '\0';            /*将数组构造成字符串形式*/
                data_output[index_data] = atoi(buffer);   //将提取出来的数据转换成int型存放在处理好的数组中

                memset(buffer,0,sizeof(buffer)/sizeof(char));
                step++;
                index_raw_data++;
                index_buffer++;
                index_buffer = 0;               //重置buffer索引
                index_data++;
            }break;
            case EDGE:
            {
                if(data_input[index_raw_data] == ';')
                {
                    step++;
                }
                else
                {
                    buffer[index_buffer] = data_input[index_raw_data];
                    index_raw_data++;
                    index_buffer++;
                }
            }break;
            case BRANCH:
            {
                buffer[index_buffer] = '\0';            /*将数组构造成字符串形式*/
                data_output[index_data] = atoi(buffer);   //将提取出来的数据转换成int型存放在处理好的数组中

                memset(buffer,0,sizeof(buffer)/sizeof(char));
                step++;
                index_raw_data++;
                index_buffer++;
                index_buffer = 0;               //重置buffer索引
                index_data++;
            }break;
            case END:
            {
                if(data_input[index_raw_data] == 0)
                {
                    run_flag = 0;
                }
                else
                {
                    step = 0;
                }
            }
        }
    }

}

5 链接

5.1 演示视频

https://www.bilibili.com/video/BV1e14y1P7Aa/?spm_id_from=333.999.0.0&vd_source=e240c7dafa7cf5d1b5ebfa7d64e9b941

5.2 源码链接

https://gitee.com/HouEna/campus-tour-guide-system

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构——校园导游系统 的相关文章

  • 构造函数的调用规则

    在面向对象编程中 xff0c 构造函数是用于创建对象的特殊函数 构造函数的调用规则如下 xff1a 当创建一个对象时 xff0c 会自动调用该对象的构造函数 构造函数的名称必须与类的名称相同 构造函数可以有多个重载版本 xff0c 但是必须
  • 深拷贝与浅拷贝

    深拷贝和浅拷贝是指在计算机编程中 xff0c 当需要复制一个对象时 xff0c 复制出来的副本与原对象之间的关系的不同 浅拷贝是指在复制对象时 xff0c 只复制了对象本身的值 xff0c 而没有复制对象包含的子对象 也就是说 xff0c
  • C++三种继承方式的区别

    访问方式分为两种 xff0c 一种是类内访问 xff0c 还有一种是类外访问 xff1b 所谓类内访问 xff0c 就是类内的函数中是使用了属性 xff1b 类外访问 xff0c 就是新建一个实例对象 xff0c 并访问这个对象的属性 xf
  • python爬虫之数据解析(BeautifulSoup)

    BeautifulSoup也是python爬虫常用的一种数据解析方法 xff0c 主要就两步 1 实例化一个Beautifulsoup对象 xff0c 平且将页面源码数据加载到该对象中 2 通过调用Beautifulsoup对象中相关的属性
  • 蓝桥杯嵌入式第十四届省赛题目解析

    前几天刚刚参加完第十四届的省赛 xff0c 这届题目比我想象中的要难 xff0c 其实想一想这也是应该的 xff0c 以前的知识点都被摸透了 xff0c 也是需要加入新的知识点了 xff0c 但是我还是想说能不能别在我参加的时候加大题目难度
  • dockerfile构建

    2 简答题 编写Dockerfile制作镜像 xff0c 生成镜像名为my build Nginx2 首先创建目录dockerfile nginx2 xff0c 保存Dockerfile文件 具体要求如下 xff1a 1 基于镜像cento
  • 安装虚拟机之后怎么配置虚拟环境、深度学习、深度强化学习环境安装

    安装步骤目录 一 配置虚拟机VMware安装包 amp Ubuntu的光盘映像文件 xff1a VMware安装Ubuntu安装 二 进入虚拟机配置环境深度 xff08 强化 xff09 学习环境的配置1 得知系统所自带python版本 x
  • 力扣-刷题方法总结(测试文章)

    知乎方面收集到的资料 xff08 非原创 xff0c 题主只是对其进行统一的整理 xff0c 方便后续查看 xff09 算法训练讲究循序渐进 xff1a 1 先从简单开始 xff0c 然后过度到中等 xff0c 再过渡到困难的进程 2 如何
  • 文件分隔符 ‘/‘(斜杠) 和 ‘\‘(反斜杠) 的使用

    前言 在学习时 xff0c 总会用到 Windows 和 Linux xff0c 输入路径时 xff0c 文件路径分隔符有时用 xff08 斜杠 xff09 xff0c 有时用 xff08 反斜杠 xff09 xff0c 属实不好区分 xf
  • VMware虚拟机安装Win11教程(解决常见报错)

    前言 今天闲来无事 xff0c 就想着装一下最新版的win11玩一下 xff0c 然后来来去去还是折腾了一些时间 xff0c 有遇到一些错误不过最好都找到了解决办法 xff0c 下面我就分享一下VMware虚拟机安装win11的详细步骤 V
  • vue打包后neditor不显示了

    原因是vue和vue template compiler 1 两者的版本不一致 xff1b 2 两者的版本低了 xff1b 例如 xff1a 我出问题的版本是 34 vue 34 34 2 5 10 34 34 vue template c
  • 【Docker常用命令】

    Docker常用命令 xff08 学习笔记 xff09 一 Docker基础命令二 Docker镜像命令三 Docker容器命令3 1 运行容器3 2 退出容器3 3 查看容器进程 xff0c 日志3 4 再次进入容器3 5 容器启停3 6
  • OpenCV学习——ArUco模块

    前提介绍 xff1a ArUco模块是OpenCV的contrib拓展库中一个模块 xff0c 需要安装OpenCV的 contrib拓展库 才能正常使用 ArUco标记 xff1a ArUco 标记是由 宽黑色边框 和 确定其标识符 xf
  • 【Vue】报错:Avoid mutating a prop directly since the value will be overwritten whenever the parent

    当我们直接改变父组件的 props 值是会报以下警告 xff1a Vue warn Avoid mutating a prop directly since the value will be overwritten whenever th
  • 深蓝学院-机器人运动规划学习笔记-第一章

    第一课 移动机器人运动规划 Motion planning for mobile robots IntroductionCourse outlineTypical planning methods overviewMap represent
  • opencv+python实战日记 入门篇(八)色块识别

    色块识别 import cv2 import numpy as np frameWidth 61 640 frameHeight 61 480 cap 61 cv2 VideoCapture 0 获取摄像头 cap set 3 640 ca
  • highway_env中自定义环境

    前言 highway env中集成了很多强化学习或者控制算法测试的驾驶环境 xff0c 但很多时候我们需要依据需求对环境进行自定义 xff0c 这里给出了自定义环境的一些步骤 xff0c 主要是基于gym 61 61 0 26版本 创建步骤
  • 深度相机和激光雷达的融合标定(Autoware)

    深度相机和激光雷达是智能汽车上常用的传感器 但深度相机具有特征难以提取 xff0c 容易受到视角影响 激光雷达存在数据不够直观且容易被吸收 xff0c 从而丢失信息 因此在自动驾驶领域 xff0c 需要对于不同传感器做数据的融合和传感器的标
  • 基于OpenCv和ROS的图像灰度化处理

    直接调用opencv灰度化函数 xff0c 对于本地图像进行处理 实现C 43 43 代码如下 xff1a 图像灰度化 include lt iostream gt cv cvtColor头文件 include lt opencv2 img
  • IMU的轨迹解算和航迹显示

    基于ros操作系统 xff0c 调用IMU数据包 xff0c 利用数据解算小车运动的轨迹 xff0c 并在rviz中实现轨迹的可视化 其中IMU四元数对于位移速度和加速度的转换 轨迹解算和换机显示的代码 xff1a IMU航迹推算 incl

随机推荐

  • 对IMU数据进行卡尔曼滤波

    我们要使用IMU数据 xff0c 必须对数据进行预处理 xff0c 卡尔曼滤波就是很好的方式 1 卡尔曼滤波 卡尔曼滤波 xff08 Kalman filtering xff09 是一种利用线性系统状态方程 xff0c 通过系统输入输出观测
  • PHPExcel导出导入问题”continue” targeting switch is equivalent to “break”.Did you mean to use “continue 2”?

    在 php 7 3 的 switch 中使用 continue 会出现警告 1 2 3 最好的方式是把 PHPExcel Shared OLE php 文件中的 continue 改为 continue 2 或 break 亲测 xff0c
  • 强化学习highway_env代码解读

    写在前面 作为强化学习的新手 xff0c 写这个系列的博客主要是为了记录学习过程 xff0c 同时也与大家分享自己的所见所想 前段时间发布了人生第一篇博客 xff0c 是关于highway env的自定义环境 但博客主要是关于如何创建一个自
  • Highway_env(Intersection)修改离散动作空间

    前言 在十字路口环境中 xff0c 主车默认的动作空间是以5m s变化的加减速以及保持原速三个动作 有时候为了学习更优化的策略 xff0c 同时与自己设置的奖励函数吻合 xff0c 需要修改环境的动作空间 这里我们主要添加两个较小加速度的纵
  • 离散动作的修改(基于highway_env的Intersection环境)

    之前写的一篇博客将离散和连续的动作空间都修改了 xff0c 这里做一下更正 基于十字路口的环境 xff0c 为了添加舒适性评判指标 xff0c 需要增加动作空间 xff0c 主要添加两个不同加速度值的离散动作 需要修改以下几个地方 xff1
  • VM 导入.ova/.ovf,未通过 OVF 规范一致性或虚拟硬件合规性检查.

    今天在用虚拟机VM导入ubuntu riscv ova文件新建Ubuntu时报错 xff1a 未通过OVF规范一致性或虚拟硬件合规性检查 网上查了一下 xff0c 了解到这是因为VM内置的Ofvtool工具的版本较低导致的 xff0c 解决
  • 借助FileZilla实现Ubuntu和 Windows之间文件互传

    借助FileZilla实现Windows和 Ubuntu间文件互传 xff0c 需要使用 FTP服务 xff0c 设置方法如下 xff1a 1 Windows下安装FTP客户端 FileZilla xff08 该软件免费 xff0c 可以直
  • 使用Ubuntu系统中的gparted工具对Ubuntu磁盘扩充

    最近在使用Ubuntu时 xff0c 发现经常提示内存空间不足 就总结了扩充Ubuntu内存的主要流程 xff0c 操作步骤如下 xff1a 第一步 xff1a 在虚拟机操作界面 xff08 关闭要进行磁盘扩充的Ubuntu xff09 进
  • 通过挂载的方式,解决由于权限无法将rootfs直接拷贝到SD卡的EXT4分区的问题

    最近在使用SD卡制作Linux启动文件时 xff0c 要将自己制作的根文件系统 xff08 rootfs xff09 拷到SD卡的EXT4分区时 xff0c 发现由于权限问题无法直接拷贝 xff0c 现通过挂载的方式解决该问题 xff0c
  • RISC-V架构下,Busybox工具的安装

    今天在RISC V架构下安装Busybox工具箱时 xff0c 找了很多的资料 xff0c 但都是ARM架构下的安装教程 xff0c 尽管内核不同但有一定的参考价值 xff0c 安装完成后对RISC V下Busybox工具箱的安装过程做出了
  • 串行通信协议小结(Serial Protocols)(1)

    通信关键点 同步通信 xff08 例如SPI xff09 双方之间的数据传输使用公共时钟信号进行同步 xff0c 数据以稳定的流传输 不需要额外的比特来标记传输的每个数据块的开始 结束 xff0c 因此速度更快 异步通信 xff08 例如U
  • MATLAB课程笔记(二)——MATLAB基础知识

    MATLAB系统环境 MATLAB操作界面的组成 采用与office 2010相同风格的操作界面 gt gt 命令提示符表示MATLAB处于准备状态 xff1a 续行符 MATLAB的搜索路径 gt gt clear 清除工作区的全部变量
  • SVN的日常使用

    1 已经加入ignore的文件夹 xff0c 解除方法 xff1a 直接到被ignore的位置 xff0c 执行 xff1a svn add lt 你被ignore的文件名 gt no ignore no ignore是取消忽略 如果是ad
  • RM遥控器接收程序的分析

    由遥控器接收分析串口与DMA RM的遥控器在使用的过程中在大体上可以分成两个部分 xff1a 信息的接收 与 信息的解析 xff0c 在信息的接收中主要用到了串口的空闲中断和DMA双缓冲区接收在本篇的信息接收部分主要根据RM官方给出的代码来
  • robomaster麦轮运动解算

    1 资源与代码 1 1 参考文章 本文主要参考的三篇文章如下 xff1a 麦轮运动特性分析 xff1a https mp weixin qq com s biz 61 MzI3MTIyMjQwNQ 61 61 amp mid 61 2247
  • FreeRTOS内核——任务与任务切换

    2 任务 相关函数 1 xTaskCreateStatic 2 prvInitialiseNewTask 3 prvInitialiseTaskLists 4 vTaskStartScheduler 5 xPortStartSchedule
  • FreeRTOS应用——任务

    12 任务 12 1 相关函数 12 1 1 任务创建函数与启动调度 12 1 1 1 xTaskCreateStatic 静态创建任务 if configSUPPORT STATIC ALLOCATION 61 61 1 TaskHand
  • FreeRTOS应用——消息队列

    13 消息队列 消息队列是一种常用于任务键通信的数据结构 xff0c 队列可以在任务与任务间 中断与任务间传递信息 xff0c 实现了任务接收来自其他任务或者中断的不定长数据 任务能从队列中读取信息 xff0c 当队列中的消息为空时 xff
  • RoboMaster电机驱动

    1 硬件 1 1 电机 RM有很多不同型号的电机 xff0c 不同型号的电机有它不同的用途 xff0c 但是以用途分类的话主要是分成两种电机 xff1a 用来精准控制位置的电机 xff0c 在RM中的主要是云台电机 RM官网上的云台电机只有
  • 数据结构——校园导游系统

    校园导游系统 1 要求 大二下学期修了数据结构这门课 xff0c 课设的要求是做一个校园导航系统 详细的要求如下 问题描述 xff1a 当我们参观校园时 xff0c 会遇到如下问题 xff1a 从当前所处位置去校园另外一个位置 xff0c