描述

这一篇介绍磁条机器人的算法思路,并开源部分代码

算法核心思路

磁条机器人的算法核心思路很简单,我愿意称之为“消息触发型”,意思就是机器人接收消息再通过代码进行判断和操作。

当然了,整体代码还是要分几部分去实现。以下几个小节来概述

消息处理

  1. 指令处理
    上位机发送过来的命令,需要将命令解析,并对命令进行处理,令机器人做出相应的改变。

  2. 磁条信号处理
    磁传感器的数据,由算法来判断磁条现在和机器人的位姿状态,改变左右电机的转速

  3. RFID模块信息处理
    RFID读到卡片后会产生中断信号,算法根据读到的卡片信息,来做相应的改变

  4. 超声信息处理
    超声模块不停的将正前方距离数据返回,算法根据数值大小,判断机器人是否遇到障碍物

状态维护

  1. 机器人自身信息
    需要维护的数据信息有很多,这些信息都需要在运行过程中被维护,包括机器人自身的状态、上一时刻的状态、左右电机的当前转速,设置的最大转速、运行方向、收到的命令、是否有目标点、目标点卡号等等。
    不一一列出,这些在代码中或通过全局常量或通过struct进行动态调整,总之将全部的信息维护好很重要,这样即使出现问题我们可以知道机器人究竟发生了什么。

  2. 状态切换
    机器人在运行过程中,最重要的就是状态。机器人出现问题,我们常问的也是,机器人现在是什么状态啊。因此机器人状态机的切换是尤为关键的。
    机器人遇到什么状况转换成什么状态机,除了改变算法中的一些变量,另外一个要做的就是改变状态灯。这让我们能够很好的判断机器人的宏观运行状态。
    为此我们另开一个小节,专门来讲一下我算法设置的机器人状态

状态介绍

我的算法中,将机器人状态分为4类,分别是READY,BLOCK,RUNNING,DERAILED,它们分别代表的顾名思义就是准备运行、有障碍、正常运行、脱轨。机器人在运行过程中的全部状态,都可以分为这四类。

(以下涉及到的命令会在之后的篇章中做出解释)

  1. READY
    含义:机器人准备好去运行
    状态转入:机器人开机后,到达指定目标后,接收到stop命令后,会进入READY状态
    状态转出:在这一状态下,机器人收到start和to两种命令后,会转到状态RUNNING
    灯:机器人常亮青色灯

  2. BLOCK
    含义:机器人被障碍物卡住
    状态转入:目前只有超声传感器的数据能令机器人进入BLOCK状态
    状态转出:前方障碍物消除后,BLOCK状态会自动恢复到阻塞之前的状态,不用人为干预
    灯:机器人常亮黄色灯

  3. RUNNING
    含义:机器人正常运行
    状态转入:收到start和to的前进命令后,而当前的状态为READY时,才会进入RUNNING;或由BLOCK恢复
    状态转出:会因障碍物转为BLOCK,会因脱轨转为DERAILED,会因到达目标转为READY
    灯:机器人常亮绿色灯

  4. DERAILED
    含义:机器人脱轨
    状态转入:目前只有磁条传感器的数据能令机器人进入DERAILED 状态,只要磁传感器返回数据证证明没有检测到磁条,READY、BLOCK和RUNNING状态都可以转为DERAILED状态
    状态转出:状态不可自动恢复,只有通过开关机将状态转为READY;或者经人为处理矫正后发送命令“start”命令,可以转为RUNNING
    灯:机器人常亮红色灯

    添加代码文件

    添加.h文件

    点击菜单栏中的魔法棒

    在弹出的窗口找到“Include Paths”,在文件编辑框最后有一个“…”的按钮,点击

    可以进入头文件添加的窗口,我们在这里面可以添加需要用到的头文件

    添加.c文件

    找到最左侧的项目文件树,可以发现“Application/User/Core”

    右键点击后,选择“Add Existing files…”,就可以添加已经写好的.c文件了

代码详解

我们首先增加状态灯的两个文件,分别是ws2812.h和ws2812.c

  • ws2812.h
#ifndef __WS2812_LED_H__
#define __WS2812_LED_H__

#include "tim.h"

#define ONE_PULSE 54
#define ZERO_PULSE 26

#define LED_NUM 60
#define LED_DATA_LEN 24
#define RESET_PULSE_LEN 80  
#define DATA_BUFFER_LEN RESET_PULSE_LEN+(LED_DATA_LEN*LED_NUM)

uint16_t static data_buffur[DATA_BUFFER_LEN] = { 0 };

void led_init(void);
void led_on(void);
void led_set(uint8_t led_id, uint8_t value_r, uint8_t value_g, uint8_t value_b);


#endif
  • ws2812.c
#include "ws2812.h"

void led_init(void)
{
    for(uint8_t i = 0; i < LED_NUM; i++)
    {
        led_set(i, 0x00, 0x00, 0x00);
    }
}

void led_set(uint8_t led_id, uint8_t value_r, uint8_t value_g, uint8_t value_b)
{
    uint16_t* p = (data_buffur + RESET_PULSE_LEN) + (LED_DATA_LEN * led_id);
    for (uint16_t i = 0; i < 8; i++)
    {
            p[i]      = (value_g << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
            p[i + 8]  = (value_r << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
            p[i + 16] = (value_b << i) & (0x80)? ONE_PULSE: ZERO_PULSE;
    }
}

void led_on(void)
{
     HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1, (uint32_t *)data_buffur, DATA_BUFFER_LEN);
}

这两个文件的代码,将被主控用来控制状态灯的颜色。

总结

这篇文章暂时介绍到这儿,除了介绍算法的整体思路,还介绍了状态灯的代码文件。
接下来还有一篇文章,我们将全部的代码放到下一篇文章中,这样便于阅读

写的不容易,欢迎各位朋友点赞并加关注,谢谢!