# 相扑机器人--前言

这一章我们来编程一个相扑机器人,机器人放置在胶带围起来的环形区域内。通过超声波传感器来检测“敌人”,并将它推到区域外。

在正式教程前,我们重新认识一下函数,之前我们已经使用过map,abs,min,max这些函数,函数就是将常用的代码包装起来。我们第一个要包装的是读取传感器的代码:

OseppRangeFinder U(2);
int P = 0;   //开机时记录的纸张感应值作为参考
boolean SR;  //右边传感器是否在胶带上
boolean SL;  //左边传感器是否在胶带上
boolean SU;  //是否发现目标
void readSensor() {
    //对于胶带的判断比较随意
    //只要和纸的参考值差距300以上,就判定为在胶带上。宁可信其有。
    //如果机器人太敏感,把300加大。保持纸张干净。
    SR = abs(analogRead(A0) - P) > 300;
    SL = abs(analogRead(A1) - P) > 300;
    SU = U.ping() < 600;  // 600毫米内有东西就判定为发现目标
}
1
2
3
4
5
6
7
8
9
10
11
12
13

代码定义了一个函数readSensor,函数名字可以随便起,自己看到函数名知道含义就可以了(最好是别人看到也能猜到),名字的要求跟变量名一样。它不像map,abs这些函数有返回值,所以函数的类型时void,表示没有返回值(我们在函数内修改了变量作为结果),像Arduino的setuploop都是void类型函数。有返回值的函数把void改为返回值类型,在函数内用return返回,例如

int myAbs(int value){
    if(value < 0){
        return -value;//立刻返回,不会再执行函数内的其他代码
    }
    return value;//如果函数有返回值,要保证任何分支内都有return语句。
}
1
2
3
4
5
6

函数可以带有参数,例子中的int value就是参数。参数只能再函数内使用。也可以在函数内定义变量,称为局部变量,局部变量有作用域,只在“{...}”范围内起作用。例如:

int mid(int pin1, int pin2) {
    int t;  // t只在函数的{}内起作用
    {  //随时都可以用{}来定义一个区域,外部看不到里面的变量
        int v1;
        int v2;
        v1 = analogRead(pin1);
        v2 = analogRead(pin2);
        t = (v1 + v2);  //内部可以使用外部的变量t
    }
    //在这里不能使用v1,v2
    return t / 2;// return的值用表达式也可以
}

int M = 0;  //这里定义全局变量M,但是只有下面的代码能用它。上面的代码不能使用

void setup() {
    int M;    //局部变量可以跟外部的名字相同,但有更高的优先级
    M = 100;  //所以这里设置的是局部变量M,全局变量M没有改变
    Serial.begin(115200);
}

void loop() {
    //在这里不能使用pin1,pin2,v1,v2,t
    Serial.println(M);  //输出0
    M = mid(A1, A0);    //计算A1,A0两个传感器的中间值
    Serial.println(M);  //输出中间值
    while (1);          //用死循环模拟停机
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

局部变量在每次进入函数时,如果没有设置,值是不确定的,如果想要像全局变量一样,要加上static关键字,称为静态变量,除了只能在定义它的{}范围内使用,其他性质跟全局变量一样。例如:

int seq(){
    //只在程序启动的时候设置初始值,进入函数时不会重复设置初始值。
    static int n=0;
    n++;  //自增,表示n=n+1,同n+=1 相同功能
    return n;
}
int notsure(){
    int n=0;  //如果局部变量不初始化,值是不确定的
    n++;
    return n;
}
void setup(){
    Serial.begin(115200);
    Serial.println(seq());//打印1
    Serial.println(seq());//打印2
    Serial.println(notsure());//打印1
    Serial.println(notsure());//打印1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

函数参数可以有默认值,例如:

int number(int what=0){
    return what;
}
void loop(){
    int one=number(1);  //参数为1,返回1
    int zero=number();  //默认参数为0,返回0
}
1
2
3
4
5
6
7

一个程序内可以有多个同名字的函数,但是函数的参数必须不同,叫做函数重载,例如

int myMax(int a, int b) {
    if (a > b)
        return a;
    else
        return b;
}
int myMax(int a, int b, int c) {
    //只给两个参数将调用前面定义的myMax
    int t = myMax(a, b);
    return myMax(t, c);
}
void loop() {
    myMax(2, 3);     //调用第一个myMax
    myMax(2, 3, 4);  //调用第二个myMax
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15