在填坑之前,再夹带一点私货,最近参加了RoboMaster加机甲大师校赛(很初级的校赛,真正的大型RoboMaster比赛级别是很高的,像我这种没有从一开始打基础的就不用想了),想写篇文章记录一下经过和收获,分享一下学到的东西。
起源
实际上我参加这个比赛是一次偶然的机会。一开始,先是我的一个小学弟,他找到了跟我同级同部门的一位老学长。比赛分为两项,RoboSpeed(自动循迹小车)和RoboBall(遥控小车推球),因此需要两个程序来负责。于是他找到我,问我要不要参加。我的回答是“行,我再了解了解吧~”,因为不是很了解,不知道如何控制单片机小车,就像先推辞一下。结果,几分钟后,我被拉进了RoboMaster小组群......既来之则安之,那就好好了解一下吧。
成果分享
小车机械部分的组装不是我的强项,我基本只负责代码和程序。talk is cheap, show me the code,先放上注释详尽的代码,再分享一些我们遇到的坑。
我们选用的开发板是arduino uno
开发板,它的语法跟C语言相近,并且可以很方便的从官网下载到IDE,并将写好的代码编译,一键烧录到开发板中。
先贴上战果稍微好一些的遥控小车的代码。遥控小车使用的是HC-05
蓝牙模块,使用手机进行遥控。另外还有一种方案,使用PS2手柄进行遥控,但在实际表现上来看,使用手机+蓝牙模块的效果并不比PS2手柄的效果差,而价格却比PS2手柄+专用信号接收器低很多。
Code Here:
#include <Servo.h>
//定义控制信令
#define STOP 0
#define FORWARD 1
#define BACKWARD 2
#define TURNLEFT 3
#define TURNRIGHT 4
#define JIASU 5
//在开发板上的输入输出管脚
int Left_motor_back = 5; //左电机后退(IN1)
int Left_motor_go = 3; //左电机前进(IN2)
int Right_motor_go = 6; // 右电机前进(IN3)
int Right_motor_back = 9; // 右电机后退(IN4)
int cmd;//控制信令
void setup()
{
//初始化电机驱动IO为输出方式
pinMode(Left_motor_go, OUTPUT);
pinMode(Left_motor_back, OUTPUT);
pinMode(Right_motor_go, OUTPUT);
pinMode(Right_motor_back, OUTPUT);
Serial.begin(9600);
}
//小车的基本动作
void goStraight() // 前进
{//转速是由输入和输出正负两极的电势差决定的
analogWrite(Left_motor_go, 110); // 左电机int,PWM比例0~255调速,左右轮差异略增减
analogWrite(Left_motor_back, 0); //右电机out,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_go, 110); //右电机in,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_back, 0); //右电机out,PWM比例0~255调速,左右轮差异略增减
}
void brake() //刹车,停车
{
analogWrite(Left_motor_go, 0); //电势差为0,停止
analogWrite(Left_motor_back, 0); //电势差为0,停止
analogWrite(Right_motor_go, 0); //电势差为0,停止
analogWrite(Right_motor_back, 0); //电势差为0,停止
}
void left() //左转(左轮不动,右轮前进)
{
analogWrite(Left_motor_go, 0); //左轮不动
analogWrite(Left_motor_back, 0); //左轮不动
analogWrite(Right_motor_go, 120); //右电机in,PWM比例0~255调速
analogWrite(Right_motor_back, 0); //右电机out,PWM比例0~255调速,左右轮差异略增减
}
void right() //右转(右轮不动,左轮前进)
{
analogWrite(Left_motor_go, 120); //左轮不动
analogWrite(Left_motor_back, 0); //左轮不动
analogWrite(Right_motor_go, 0); //右电机in,PWM比例0~255调速
analogWrite(Right_motor_back, 0); //右电机out,PWM比例0~255调速
}
void back() //后退
{
analogWrite(Left_motor_go, 0); //左电机in,PWM比例0~255调速,左右轮差异略增减
analogWrite(Left_motor_back, 110); //左电机out,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_go, 0); //右电机in,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_back, 110);
}
void jiasu() //加速前进
{
analogWrite(Left_motor_go, 175); // 左电机in,PWM比例0~255调速,左右轮差异略增减
analogWrite(Left_motor_back, 0); // 左电机out,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_go, 175); //右电机in,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_back, 0); //右电机out,PWM比例0~255调速,左右轮差异略增减
}
//循环运行代码
void loop()
{
char cmd = Serial.read(); //读取蓝牙模块发送到串口的数据
switch (cmd) { //switch判断
case FORWARD: {
goStraight();
break;
}
case TURNLEFT: {
left();
break;
}
case BACKWARD: {
back();
break;
}
case TURNRIGHT: {
right();
break;
}
case STOP: {
brake();
break;
}
case JIASU: {
jiasu();
break;
}
}
}
遇到的坑:
- 不要单纯的以为亚克力板多么结实。我们的小车车身材料和推球的铲子的材质为亚克力,结果在决赛中车身撞到墙上,把推球的铲子撞掉了,推球能力下降,不敌对方。进入了决赛,可以考虑用金属车身。
- 转弯时,不能让车绕车中心旋转来进行,否则无法推球。必须让一侧的轮子停下或减小转速转弯。
- 操控车辆时,不要操之过急,切记在靠近球门时高速运球,防止把进球区的球撞出,或者让球装上墙壁反弹出进球区。
- 烧录程序时,要把HC-05模块从开发板上拔下,否则蓝牙模块可能无法正常工作。
下面是循迹小车的代码。
循迹小车就...比较玄学。在测试场地上,红外传感器比较灵敏,灰度传感器基本没法使用;而比赛场地却恰恰相反。传感器的灵敏度调整也是个费时间的活,比赛前一定要提早调整到最佳状态。
这种小车的思路有很多,最终我们选择了这种三传感器方案:左右传感器检测到白色,中间是黑色时,正常前进;左中白,右边黑或者左白,右中黑则向右转;右中白,左边黑或者右白,左中黑则向右转;都是白色则说明前进的太多,向后后退;都是黑色说明到达终点,小车停止。
我们还遇到了奇怪的问题:相同的电机,相同的电势差,转速却不同,所以程序中进行了矫正,速度根据电机和电池的不同情况自行填写测试吧。
Code Here,部分注释见上面的代码:
#include <Servo.h>
int Left_motor_go = 3; //左电机前进(IN2)
int Left_motor_back = 5; //左电机后退(IN1)
int Right_motor_go = 9; // 右电机前进(IN3)
int Right_motor_back = 6; // 右电机后退(IN4)
const int SensorMiddle = 12; //中循迹红外传感器
const int SensorLeft = 10; //左循迹红外传感器
const int SensorRight = 11; //右循迹红外传感器
int SL; //左循迹红外传感器状态
int SR; //右循迹红外传感器状态
int SM; //中循迹红外传感器状态
void setup()
{
//初始化电机驱动IO为输出方式
pinMode(Left_motor_go, OUTPUT); // PIN 5(PWM)
pinMode(Left_motor_back, OUTPUT); // PIN 6 (PWM)
pinMode(Right_motor_go, OUTPUT); // PIN 9 (PWM)
pinMode(Right_motor_back, OUTPUT); // PIN 10 (PWM)
pinMode(SensorRight, INPUT); //定义右循迹红外传感器为输入
pinMode(SensorLeft, INPUT); //定义左循迹红外传感器为输入
pinMode(SensorMiddle, INPUT); //定义中循迹红外传感器为输入
}
//小车的基本动作
void goStraight() // 前进
{
analogWrite(Left_motor_go, 45); // 左电机前进,PWM比例0~255调速,左右轮差异略增减
analogWrite(Left_motor_back, 0);
analogWrite(Right_motor_go, 63); //右电机前进,PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_back, 0);
Serial.println("Go");
delay(30);
}
void brake() //刹车,停车
{
analogWrite(Right_motor_go, 0);
analogWrite(Right_motor_back, 0);
analogWrite(Left_motor_go, 0);
analogWrite(Left_motor_back, 0);
Serial.println("stop");
}
void left() //左转(左轮不动,右轮前进)
{
digitalWrite(Left_motor_go, 0); //左轮不动
digitalWrite(Left_motor_back, 0);
analogWrite(Right_motor_go, 85); //右电机前进,PWM比例0~255调速
analogWrite(Right_motor_back, 0);
Serial.println("left");
delay(25);
}
void right() //右转(右轮不动,左轮前进)
{
analogWrite(Left_motor_go, 62);
analogWrite(Left_motor_back, 0); //左电机前进,PWM比例0~255调速
analogWrite(Right_motor_go, 0); //右电机不动
analogWrite(Right_motor_back, 0);
Serial.println("right");
delay(25);
}
void back() {
analogWrite(Left_motor_go, 0);
analogWrite(Left_motor_back, 42); //左电机前进,PWM比例0~255调速
analogWrite(Right_motor_go, 0); //右电机不动
analogWrite(Right_motor_back, 58);
Serial.println("back");
delay(20);
}
//循环运行代码
void loop()
{
//有信号为LOW/0 没有信号为HIGH/1
SR = digitalRead(SensorRight);
SL = digitalRead(SensorLeft);
SM = digitalRead(SensorMiddle);
if (SL == 0 & SM == 1 & SR == 0)
goStraight(); //调用前进函数
else if (SL == 1 & SM == 0 & SR == 0)
right();
else if (SL == 0 & SM == 0 & SR == 1)
left();
else if (SL == 0 & SM == 0 & SR == 0)
back();
else if (SL == 1 & SM == 1 & SR == 1)
brake();
else if (SL == 0 & SM == 1 & SR == 1)
left();
else if (SL == 1 & SM == 1 & SR == 0)
right();
}
遇到的坑:
- 如果有财力,一定要买动力更好的减速电机。普通的TT电机,电压不够就无法启动,电压足够之后速度又太快,不好控制速度。电池使用两节18650足矣。
- 灰度传感器和红外传感器传回的黑白数据刚好相反,如果使用灰度传感器的话,在loop()函数中将0和1互换即可。
收获
这次参加的RoboMaster加机甲大师校赛还是有很多收获的,得到了学分,收获了arduino uno
开发板相关的知识,加强了我们小团队之间的了解。趁着我目前还有些空余的时间,这样的活动还是要多多参加的好。
赞一个,小马哥贼溜