智能车视觉处理系列文章——迷宫巡线法详解(含代码)

巡线方法

1.迷宫巡线法

​ 按照上海交通大学的方法,通过使用迷宫巡线(种子生长法)在巡线的过程中进行图象的自适应阈值,这样可极大的减少计算量。具体思想参考上交文章,以下内容为对代码(以左手迷宫巡线为例)的解析。

  • 首先我们将**灰度图(时刻谨记)**作为输入,根据经验取图像下方中间偏左一点作为起始点(注意该点要在白色的赛道上),我们依次向左取像素点,直到像素点的值超过某一值(代码中thres=140),则认为该点已经靠近赛道。

    // 原图找左右边线
    int x1 = img_raw.width / 2 - begin_x, y1 = begin_y;
    ipts0_num = sizeof(ipts0) / sizeof(ipts0[0]);
    for (; x1 > 0; x1--)
        if (AT_IMAGE(&img_raw, x1 - 1, y1) < thres)  // 寻找边线起始点
            break;
    if (AT_IMAGE(&img_raw, x1, y1) >= thres)
        findline_lefthand_adaptive(&img_raw, block_size, clip_value, x1, y1, ipts0, &ipts0_num);
    else
        ipts0_num = 0;  // 如果没有找到像素点大于thres(140)的,则会将ipts_num置为0.
    

    在这里插入图片描述

  • 参考上交文档,“小明”要一直左手扶墙(勿忘)脚踩白色像素点,所以“小明“面临三种情况,同时要确保左手扶墙”小明“在三种情况下,分别有三种确定对策:

    1. 前进方向像素为黑——向右转。
    2. 前进方向像素为白,且左前方像素为白(墙角)——斜着走(x,y值均改变),且向左转。
    3. 前进方向像素为白,左前方像素为黑——向前走。
  • 代码及参数解释:

    参数解释

    • image_t * img:存放图像相关数据的指针。
    • block_size:自适应阈值的范围。
    • clip_value:经验值,防止出现强制分割的现象(一般为2~5)。
    • x:”小明“出生位置x坐标。
    • y:"小明"出生位置y坐标。
    • int pts[ ][2]用来存放边线数据。
    • int *num:存放边线像素点的个数。

    代码

    /* 前进方向定义:
     *   0
     * 3   1
     *   2
     */
    AT_DTCM_SECTION_ALIGN_INIT(const int dir_front[4][2], 8) = {{0,  -1},
                                                                {1,  0},
                                                                {0,  1},
                                                                {-1, 0}};
    AT_DTCM_SECTION_ALIGN_INIT(const int dir_frontleft[4][2], 8) = {{-1, -1},
                                                                    {1,  -1},
                                                                    {1,  1},
                                                                    {-1, 1}};
    
    // 左手迷宫巡线
    AT_ITCM_SECTION_INIT(void findline_lefthand_adaptive(image_t *img, int block_size, int clip_value, int x, int y, int pts[][2], int *num)) 
    {
        assert(img && img->data);     // 不满足则退出执行
        assert(num && *num >= 0);
        assert(block_size > 1 && block_size % 2 == 1);
        int half = block_size / 2;
        int step = 0, dir = 0, turn = 0; // step表示前进的步数;dir通过改变索引改变当前小人朝向的方向
        while (step < *num && half < x && x < img->width - half - 1 && half < y && y < img->height - half - 1 && turn < 4) 
        {
            int local_thres = 0;
            for (int dy = -half; dy <= half; dy++)  // for循环用来计算block区域的像素值之和
            {
                for (int dx = -half; dx <= half; dx++) 
                {
                    local_thres += AT(img, x + dx, y + dy);
                }
            }
            local_thres /= block_size * block_size;
            local_thres -= clip_value;   // (x,y)点block区域内的阈值
    
            int current_value = AT(img, x, y);
            int front_value = AT(img, x + dir_front[dir][0], y + dir_front[dir][1]);
            int frontleft_value = AT(img, x + dir_frontleft[dir][0], y + dir_frontleft[dir][1]);
            if (front_value < local_thres)  // 前进方向像素为黑色
            {
                dir = (dir + 1) % 4;   // 遇到前方为黑色需要右转一次
                turn++;
            } 
            else if (frontleft_value < local_thres) // 前方像素为白色,且左前方像素为黑色
            {
                x += dir_front[dir][0];
                y += dir_front[dir][1];
                pts[step][0] = x;   // 用来存放边线坐标信息
                pts[step][1] = y;
                step++;
                turn = 0;
            } 
            else  // 前方为白色,左前方为白色(墙角)
            {
                x += dir_frontleft[dir][0];   // 遇到墙角要斜着走
                y += dir_frontleft[dir][1];
                dir = (dir + 3) % 4;   // 遇到墙角要左转一次
                pts[step][0] = x;
                pts[step][1] = y;
                step++;
                turn = 0;
            }
        }
        *num = step;
    }
    

    代码说明

    1. dir:用来控制”小明”转向,如dir = (dir + 1) % 4dir对应的索引会向下移动一位(向右转一次

    2. dir_front:存放前进步伐,dir会索引到,当前面向的前方。

    3. dir_frontleft:存放左前进的步伐,dir会索引到当前面向的左前方。(见图)

      在这里插入图片描述

    4. 代码中存在一些宏定义的名称,如AT(img, x + dx, y + dy)为求对应像素点的像素值,这些定义需要在全部代码中查找。

      在这里插入图片描述

<think>好的,用户想了解右手则在迷宫巡线中的算法实现。首先,我需要回忆右手则的基本原理,即始终沿着右侧墙壁行走,这样最终能走出迷宫,前提是迷宫是简单连通的。不过用户提到的是巡线,可能是指沿着地面上的线路行走,而不是物理迷宫。需要确认这一点,但根据上下文提供的参考代码,是关于小车走迷宫的,可能涉及传感器检测线路。 接下来,需要整理右手则的具体实现步骤。通常,这类算法会使用多个红外传感器来检测线路的位置。例如,三个传感器,左、中、右,根据它们的反馈调整方向。当右侧传感器检测到线时,可能向右转;如果中间传感器在线内,则直行,否则根据右侧是否检测到线来调整。 然后,考虑参考用户提供的代码片段,虽然代码是关于端口初始化的,但可以看出涉及到端口配置,可能用于控制电机或传感器。需要将算法代码结构结合,比如传感器的输入和电机的输出控制。 还需要考虑可能的问题,比如死循环或复杂迷宫处理。右手则在存在环路的情况下可能无有效解决,但用户提到的迷宫可能是简单迷宫。此外,代码实现中可能需要处理传感器的实时读取和电机的响应速度。 最后,生成相关问题时,需要考虑用户可能进一步想了解的内容,比如传感器的布局、遇到死胡同的处理、与其他算法的比较,以及实际应用中的调试方。确保问题覆盖算法的优化、应用场景和局限性。</think>### 右手迷宫巡线算法实现 #### 基本原理 右手则是一种基于拓扑学的迷宫导航策略:始终沿着右侧墙壁移动,适用于简单连通的迷宫结构[^1]。在巡线场景中,可简化为通过传感器检测线路位置,控制车辆沿路径右侧边界行进。 #### 算法实现步骤 1. **传感器布局**: - 使用3-5个红外传感器横向排列 - 典型配置:`[左][中左][中][中右][右]` - 检测逻辑:`1`表示检测到黑线,`0`表示白区 2. **控制逻辑**: ```python while True: sensors = read_sensors() # 获取传感器状态 if sensors[2] == 1: # 中间传感器在线内 move_forward() elif sensors[3] == 1: # 右侧传感器检测边界 turn_right(soft=True) elif sensors[4] == 1: # 极右传感器检测边界 turn_right(hard=True) else: # 丢失线路时 search_right_wall() # 执行右侧搜索模式 ``` 3. **硬件接口实现**(参考TI小车): ```c // 端口初始化(示例代码) void Port2_Init(void){ P2->SEL0 = 0x00; // 选择GPIO模式 P2->SEL1 = 0x00; P2->DS = 0x07; // 增强驱动能力 P2->DIR = 0x07; // 设置P2.0-P2.2为输出 P2->OUT = 0x00; // 初始输出低电平 } ``` #### 关键参数优化 - 转向补偿量:$θ = k_p \cdot e + k_d \cdot \frac{de}{dt}$ 其中$e$为线路偏移误差,$k_p=0.3$, $k_d=0.1$为典型值 - 采样频率:建议100-200Hz - 电机PWM占空比:直行时$D_{left}=D_{right}=60\%$,差速转向时差$ΔD≥15\%$
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值