风林火山技术文档(程序部分)

系统架构

  根据比赛规则,机器人可以遥控,也可以自动控制,考虑到搏击机器人面对的环境高度复杂,自动控制将是一个庞大繁复、任重道远的工程,故而我们一致认为机器人应主要依靠遥控,由人充当传感器和决策机构,机器人只负责将人的指令转换为具体的动作。
  在遥控方案上,我们采用了许炎武的提议——放弃制作手柄,而以一台计算机作为遥控端(因此遥控端被命名为“许炎武”)。这要求“许炎武”具有与无线模块通信的能力。经过查找,我们发现nRF24L01的扩展产品nRF24LU1可以与计算机直接通信,但此款产品在中国境内无法买到,只好作罢。最后我们令“许炎武”通过串口输出信号,自己制作了一个ATmega16+nRF24L01的电路板作为转发机构(因此被命名为“Skywalker”)。硬件结构基本解决,软件结构是:“许炎武”将人的指令进行初步翻译——将抽象的指令翻译为机器人上舵机状态的列表,通过串口传给Skywalker,Skywalker将信号原样发送出去。而在机器人上,有一块同样的nRF24L01负责接收无线信号,传送给ATmega16,然后再由ATmega16生成PWM波控制舵机(机器人端被命名为“bondsdancer”)。

实现细节
“许炎武”

  涉及串口通信的部分由libserial库完成,相关内容请查阅libserial的文档。
  “许炎武”共由三层构成。
  上层对应xuyanwu.cpp,是程序的初始化和主循环。在主循环中,程序不断的读入一行字符,每行首字符即为指令的“名称”,主循环由此决定将调用哪一个翻译函数,并将此行的所有字符传递给对应的翻译函数,翻译函数可以在这些字符中提取参数。
  中层对应translator.h和translator.cpp,提供了所有的翻译函数。每个翻译函数的模式都是生成一个舵机状态矩阵,矩阵的每一行表示某一时刻机器人上所有舵机的状态,由此决定了机器人在某一时刻的状态,而整个矩阵即完成了状态的转变,生成一个动作。矩阵的每个元素定为servo_cmd类型,包括id,alpha,speed三个成员。id表示舵机的号码;alpha对应舵机角度,但不是角度,而是由宏_(theta)生成的数值以及特殊值-1(表示“角度不变”),在bondsdancer章节详述;speed对应舵机运行速度,越大则舵机运行越慢,作用原理是将舵机的角度变换拆分为speed份,每10ms只变换1/speed,只有矩阵每一行的第一个元素的speed起作用,因为我们认为每个状态设定一个speed就够了。矩阵生成后传递给下层的cmds_sender,由cmds_sender负责编码并经串口发出,若串口或Skywalker发生故障,cmds_sender将返回false,同时会在标准输出提供错误信息。每个翻译函数都需在cmd_translators_init中注册到cmd_translators的某一个项。
  下层对应basis.h和basis.cpp,提供了servo_cmd类型和cmds_sender。basis.cpp中维护着一个舵机状态列表,并试图与机器人上舵机的状态同步更新,由此可以在servo_cmd中设置特殊值表示“角度不变”。cmds_sender是下层的核心,它遍历矩阵的每一行,将一行中第一个speed及每一个alpha成员取出,编码后传递给libserial提供的写串口函数,再从串口读入同样的数据以校验,若写入失败、读入超时、读入数据与发送数据不符则尝试关闭串口重新打开,再读入表示无线模块发送成功与否的两个字节,若发送失败则再次发送,否则更新舵机状态列表,准备读入下一行。

Skywalker

  Skywalker的结构最为简单,从USART读入固定长度的数据,分成两半。对每个“半份”,原样经USART发回给“许炎武”,再转发给nRF24L01,再将nRF24L01发送数据成功与否的标志一并发给“许炎武”。其中与nRF24L01和“许炎武”通信的函数均由一个库提供。

bondsdancer

  在制作Skywalker的过程中,我们偶然发现11.0592Mhz的晶振可以通过TC0准确的定出10ms,这样TC1就可以拿来用于控制PWM波的产生,于是我们可以把2ms均分为2764.8份,而非250份,这样我们可以更精确的控制舵机角度,这就是“许炎武”中alpha值的由来。
  当从无线模块读取到新的数据时,首先检验其是否为有效数据(判断是否能解码为一个新的舵机状态列表和speed),若为有效数据,bondsdancer将经过speed步逐步达到新的状态,否则丢弃。每一步耗时10ms,在10ms中,先根据原舵机状态和目标状态,生成当前舵机状态,然后分3次每次控制4个舵机。达到新的状态后,再次查询无线模块是否收到了新的数据。若无线模块没有收到数据,则以10ms为周期维持当前状态,并不断查询有无数据到达。

问题与解决方案

  由于我们在bondsdancer中不是采用空循环让ATmega16空闲,而是直接将其转入睡眠模式,所以睡眠模式下ATmega16能否维持IO口电压就成了一个重要问题,好在经过实验,IDLE模式既可以维持IO口电压,又可以被时钟中断唤醒,唤醒时间仅6周期。
  在制作Skywalker的过程中,我们发现使用16Mhz晶振的USART在115200bps下误码率极高,完全无可用性,后改为使用11.0592Mhz晶振,进而发现11.0592Mhz晶振可以用TC0定出10ms,于是我们所有的ATmega16都换用了11.0592Mhz晶振。
  在调试Skywalker过程中,我们发现SPI始终无法正常工作,后仔细查看ATmega16的Datasheet,发现SS端口须定向为输出。
  编写“许炎武”的过程中,我们发现居然无法一次性向串口发送多个字节。后多方查询,终于得知是CH341 USB到串口转换芯片的驱动有问题,导致若一次性向串口发送多个字节,则驱动将被置于挂起状态,于是暂时只好一个字节一个字节的向串口传送数据,最后我们买了更好的转换线,终于解决了这个问题。
  在联合调试的过程中,我们发现nRF24L01并没有传说中那么神乎其神,丢包和误码时有发生,却无可奈何,只好在软件上加大了对意外的探测和处理力度。

心情文字

  在调试无线模块的过程中,我们得到了周宇老师的热心而无私的帮助,在这里,我衷心感谢他。而无论是我取得突破还是遇到瓶颈、挫折之时(显然这种情况更多一些),队长陈凯、队友许炎武、杨言超都一如既往的支持我,并报以巨大的信任,愿上帝保佑我没有辜负他们。
  人的一生,殊途同归,终点处的景观大抵无甚可看,反而是路上的风景让人留恋。无论我们的机器人最终能否勇夺三军,这个暑假都已经被我们珍贵的记忆充实。亲爱的朋友们,感谢你们陪我游览这个美丽的花园!