分类目录归档:Arduino小组

超声波雷达-创客开源 Arduino项目

//淘宝『创元素店』https://shop423015102.taobao.com/ //更新日期 2021/03/06 //MiniRadar 超声波雷达 程序 //本程序对应商品 https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-23815833841.8.4f231fe7qvLFZi&id=649834806872

//Github版链接: https://github.com/johnsonwust/MiniRadar

include

include

include "Ucglib.h"

//显示屏的lib 如果没有该lib请按Ctrl+Shift+I 从 库管理器中搜索 ucglib,并安装

define trigPin 6 //超声波模块的Trig口 6

define echoPin 5 //超声波模块的echo口 5

define ServoPin 3 //底座舵机端口 3

int Ymax = 128; //屏幕的竖向像素数 int Xmax = 160; //屏幕的横向像素数 int Xcent = Xmax / 2; //x中位 int base = 118; //基线高度 int scanline = 105; //雷达扫描线长度

Servo baseServo; Ucglib_ST7735_18x128x160_HWSPI ucg(/cd=/ 9, /cs=/ 10, /reset=/ 8);

void setup(void) {

  ucg.begin(UCG_FONT_MODE_SOLID); //初始化屏幕
  ucg.setRotate90();              //设置成横屏  如果屏幕显示方向是反的,可以修改函数 setRotate90 或 setRotate270

  pinMode(trigPin, OUTPUT);       //设置trigPin端口模式
  pinMode(echoPin, INPUT);        //设置echoPin端口模式
  Serial.begin(115200);             //设置串口传输率
  baseServo.attach(ServoPin);     //初始化舵机

  //欢迎屏幕
  ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT);
  ucg.setColor(0, 0, 100, 0);
  ucg.setColor(1, 0, 100, 0);
  ucg.setColor(2, 20, 20,20);
  ucg.setColor(3, 20, 20, 20);
  ucg.drawGradientBox(0, 0, 160, 128);
  ucg.setPrintDir(0);
  ucg.setColor(0, 5, 0);
  ucg.setPrintPos(27,42);
  ucg.setFont(ucg_font_logisoso18_tf);  
  ucg.print("Mini Radar");
  ucg.setColor(0, 255, 0);
  ucg.setPrintPos(25,40);
  ucg.print("Mini Radar");
  ucg.setFont(ucg_font_helvB08_tf);
  ucg.setColor(20, 255, 20);
  ucg.setPrintPos(40,100);
  ucg.print("Testing...");
  baseServo.write(90);

  //测试底座的运行情况,注意检测底座位置和转动姿态,是否有卡住(或者导线缠绕)的情况。
  for(int x=0;x<180;x+=5)
      { baseServo.write(x);
        delay(50);
       }
  ucg.print("OK!");
  delay(500);

  //清屏
  //ucg.clearScreen();
  cls();
  ucg.setFontMode(UCG_FONT_MODE_SOLID);
  ucg.setFont(ucg_font_orgv01_hr);

}

void cls() { //清屏 ucg.setColor(0, 0, 0, 0);

for(int s=0;s<128;s+=8) for(int t=0;t<160;t+=16) { ucg.drawBox(t,s,16,8); // delay(1); }

}

int calculateDistance() { long duration; //trigPin断电 并 等待2微妙 digitalWrite(trigPin, LOW); delayMicroseconds(2); //trigPin加电 延时 10微妙 再断电 digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); //读取echoPin返回声波的传播时间(微妙) duration = pulseIn(echoPin, HIGH); //将回声时间转换成距离数值 return duration*0.034/2; }

void fix_font() { ucg.setColor(0, 180, 0); ucg.setPrintPos(70,14); ucg.print("1.00"); ucg.setPrintPos(70,52); ucg.print("0.50"); ucg.setPrintPos(70,90); ucg.print("0.25"); }

void fix() {

  ucg.setColor(0, 40, 0);
  //画基线圆盘
  ucg.drawDisc(Xcent, base+1, 3, UCG_DRAW_ALL); 
  ucg.drawCircle(Xcent, base+1, 115, UCG_DRAW_UPPER_LEFT);
  ucg.drawCircle(Xcent, base+1, 115, UCG_DRAW_UPPER_RIGHT);
  ucg.drawCircle(Xcent, base+1, 78, UCG_DRAW_UPPER_LEFT);
  ucg.drawCircle(Xcent, base+1, 78, UCG_DRAW_UPPER_RIGHT);
  ucg.drawCircle(Xcent, base+1, 40, UCG_DRAW_UPPER_LEFT);
  ucg.drawCircle(Xcent, base+1, 40, UCG_DRAW_UPPER_RIGHT);
  ucg.drawLine(0, base+1, Xmax,base+1);

  ucg.setColor(0, 120, 0);
  //画刻度表
   for(int i= 40;i < 140; i+=2)
   {

    if (i % 10 == 0) 
      ucg.drawLine(105*cos(radians(i))+Xcent,base - 105*sin(radians(i)) , 113*cos(radians(i))+Xcent,base - 113*sin(radians(i)));
    else

     ucg.drawLine(110*cos(radians(i))+Xcent,base - 110*sin(radians(i)) , 113*cos(radians(i))+Xcent,base - 113*sin(radians(i)));
   }

   //画一些装饰性图案 
   ucg.setColor(0,200,0);
   ucg.drawLine(0,0,0,18);
   for(int i= 0;i < 5; i++)
   {
      ucg.setColor(0,random(200)+50,0);
      ucg.drawBox(2,i*4,random(14)+2,3);
   }

   ucg.setColor(0,180,0);
   ucg.drawFrame(146,0,14,14);
   ucg.setColor(0,60,0);
   ucg.drawHLine(148,0,10);
   ucg.drawVLine(146,2,10);
   ucg.drawHLine(148,13,10);
   ucg.drawVLine(159,2,10);

   ucg.setColor(0,220,0);
   ucg.drawBox(148,2,4,4);
   ucg.drawBox(148,8,4,4);
   ucg.drawBox(154,8,4,4);
   ucg.setColor(0,100,0);
   ucg.drawBox(154,2,4,4);

   ucg.setColor(0,90,0);
   ucg.drawTetragon(62,123,58,127,98,127,102,123);
   ucg.setColor(0,160,0);
   ucg.drawTetragon(67,123,63,127,93,127,97,123);
   ucg.setColor(0,210,0);
   ucg.drawTetragon(72,123,68,127,88,127,92,123);

}

void loop(void) {

int distance;

fix(); fix_font(); //重绘屏幕背景元素

for (int x=180; x > 4; x-=2){ //底座舵机从180~0度循环

  baseServo.write(x);             //调整舵机角度

  //绘制雷达扫描线
  int f = x - 4; 
  ucg.setColor(0, 255, 0);
  ucg.drawLine(Xcent, base, scanline*cos(radians(f))+Xcent,base - scanline*sin(radians(f)));
  f+=2;
  ucg.setColor(0, 128, 0);
  ucg.drawLine(Xcent, base, scanline*cos(radians(f))+Xcent,base - scanline*sin(radians(f)));
  f+=2;
  ucg.setColor(0, 0, 0);
  ucg.drawLine(Xcent, base, scanline*cos(radians(f))+Xcent,base - scanline*sin(radians(f)));
  ucg.setColor(0,200, 0);
  //测距
  distance = calculateDistance();

  //根据测得距离在对应位置画点
  if (distance < 100)
  {
    ucg.setColor(255,0,0);
    ucg.drawDisc(distance*cos(radians(x))+Xcent,-distance*sin(radians(x))+base, 1, UCG_DRAW_ALL);
  }
  else
  { //超过1米以上的,用黄色画在边缘区域示意
    ucg.setColor(255,255,0);
    ucg.drawDisc(116*cos(radians(x))+Xcent,-116*sin(radians(x))+base, 1, UCG_DRAW_ALL);
  }

  //调试代码,输出角度和测距值  
  Serial.print(x); 
  Serial.print("    ,   ");
  Serial.println(distance); 

  if (x > 70 and x < 110)  fix_font();  //扫描线和数字重合时,重绘数字

  ucg.setColor(0,155,  0);
  ucg.setPrintPos(0,126);
  ucg.print("DEG: "); 
  ucg.setPrintPos(24,126);
  ucg.print(x);
  ucg.print("  ");
  ucg.setPrintPos(125,126);
  ucg.print("  ");
  ucg.print(distance);
  ucg.print("cm  "); 

} //ucg.clearScreen(); //清屏 如果arduino供电不足,可能会引起白屏(显示信号中断)可以用 cls();函数代替 ucg.clearScreen(); delay(50); cls(); //如有频繁白屏情况,可以使用该函数 。或者增加外部供电

fix(); fix_font(); //重绘屏幕背景元素

for (int x=1; x < 176; x+=2){
baseServo.write(x); //调整舵机角度

  //绘制雷达扫描线
  int f = x + 4;
  ucg.setColor(0, 255, 0);
  ucg.drawLine(Xcent, base, scanline*cos(radians(f))+Xcent,base - scanline*sin(radians(f)));
  f-=2;
  ucg.setColor(0, 128, 0);
  ucg.drawLine(Xcent, base, scanline*cos(radians(f))+Xcent,base - scanline*sin(radians(f)));
  f-=2;
  ucg.setColor(0, 0, 0);
  ucg.drawLine(Xcent, base, scanline*cos(radians(f))+Xcent,base - scanline*sin(radians(f)));
  ucg.setColor(0, 200, 0);
  //测距
  distance = calculateDistance();

  //根据测得距离在对应位置画点
  if (distance < 100)
  {
    ucg.setColor(255,0,0);
    ucg.drawDisc(distance*cos(radians(x))+Xcent,-distance*sin(radians(x))+base, 1, UCG_DRAW_ALL);
  }
  else
  { //超过1米以上的,用黄色画在边缘区域示意
    ucg.setColor(255,255,0);
    ucg.drawDisc(116*cos(radians(x))+Xcent,-116*sin(radians(x))+base, 1, UCG_DRAW_ALL);
  }

  //调试代码,输出角度和测距值  
  Serial.print(x); 
  Serial.print("    ,   ");
  Serial.println(distance); 

  if (x > 70 and x < 110)  fix_font();  //扫描线和数字重合时,重绘数字

  ucg.setColor(0,155,  0);
  ucg.setPrintPos(0,126);
  ucg.print("DEG: "); 
  ucg.setPrintPos(24,126);
  ucg.print(x);
  ucg.print("   ");
  ucg.setPrintPos(125,126);
  ucg.print("   ");
  ucg.print(distance);
  ucg.print("cm   "); 

} //ucg.clearScreen(); // delay(50); cls();

}

从开源硬件到开源机器人应该怎样上手-教程一

如果说Helllo World是所有编程者牙牙学语的第一课,那么Blink(闪烁)和GoGo(跑起来)应该就是硬件编程和机器人编程的第一课,这就是产品名字的来历。本套入门教程共十篇,基于专为STEM教育(科学、技术、工程、数学)而设计的Blinkgogo车型机器人进行讲解。Blinkgogo mixly 米思奇Arduino 蓝牙小车  ←点击了解Blinkgogo

 

产品拆箱后,我们接下来需要将U盘里的东西拷贝到电脑上,然后打开Mixly或者Arduino进行编程。Mixly(米思齐)是一款基于Arduino的图形化编程软件。用户可以通过拼接积木块的方式来编写程序。按照产品附带的上手手册一即可使用范例程序,并下载完成后,你就可以开始尝试深入了解以便编写自己的程序了。

首先,我们需要先让小车动起来

如果我们想让电机前进,我们该怎么操作呢?

我们先从硬件线路看起,从Blink的底盘部可以看到:

从背面看,motor A为右侧电机,motor B为左侧电机

右侧电机由端口5、10控制

左侧电机由端口6、11控制

端口5、6与电机正向相连接

端口10、11与电机负向相连接

当端口5、6的电压高于端口10、11时,形成上图所示方向电流,左、右电机正转,Blinkgogo前进。反之,左、右电机倒转,Blinkgogo后。,电压相同时,无电流形成,左、右电机停转,Blinkgogo停止

 

那现在,假设我们想让Blink

全速向前前进2秒

再向后后退2秒

最后停止

在编程界面中,我们需要调用

"输入/输出"模块中的"数字输出"模块

和“控制”模块中的"延迟"模块来实现电机的运转

 

向前前进:

使用"数字输出"模块

设置端口5为高电平,端口10为低电平(右电机正转)

使用"数字输出"模块

设置端口6为高电平,端口11为低电平(左电机正转)

使用"延迟"模块

延迟2000毫秒(即持续2秒钟)

向后后退:

使用"数字输出"模块

设置端口5为低电平,端口10为高电平(右电机倒转)

使用"数字输出"模块

设置端口6为低电平,端口11为高电平(左电机倒转)

使用"延迟"模块

延迟2000毫秒(即持续2秒钟)

 

 

停止:

使用"数字输出"模块

设置端口5、6、10、11全为低电平(A、B电机停止)

现在进阶一下

如果我们想让Blink以特定速度前行或后退

我们该怎么做?

即,更改一下之前的任务:

现在我们想让Blink

以60%的速度向前前进2秒

再以60%的速度向后后退2秒

最后停止

 

我们需要调用"输入/输出"模块中的

"模拟输出"模块

来实现电机的调速

设置电机两极中一侧为低电平

一侧为特定的电压值

便可以实现给电机设定速度

电压值的数值设置通过“模拟输出”实现

模拟输出的数值范围为0~255

模拟输出赋值为255即为全速

我们需要60%的速度

则需要将另一端口赋值为60%*255=153。

 

向前前进:

使用"数字输出"模块,设置端口10为低电平

使用"模拟输出"模块,设置端口5为153

(右电机以60%速度正转)

使用"数字输出"模块,设置端口11为低电平

使用"模拟输出"模块,设置端口6为153

(左电机以60%速度正转)

使用"延迟"模块,延迟2000毫秒

(即持续2秒钟)

 

向后后退:

使用"数字输出"模块,设置端口5为低电平

使用"模拟输出"模块,设置端口10为153

(右电机以60%速度倒转)

使用"数字输出"模块,设置端口6为低电平

使用"模拟输出"模块,设置端口11为153

(左电机以60%速度倒转)

使用"延迟"模块,延迟2000毫秒

(即持续2秒钟)

停止:

使用"数字输出"模块

设置端口5、6、10、11全为低电平(A、B电机停止)

最后,我们来讲一下转向

Blink不像我们日常开的汽车有方向盘

可以通过调整转向盘转向

在Blink中,需要用左右电机的速度差来转向

如果右电机速度大于左电机速度,Blink会向左转

左电机速度大于右电机速度,Blink向右转

转弯半径的大小也由速度差的大小决定

要注意,如一侧电机设置为后退

在计算速度差的时候,速度值以负数计算哦

关于Blink转向的探究

就留给大家课后去探索

我们将在下一节,演示不同情景的转向。

附:Arduino源代码

void setup()
{
/*
​
  11      10
   |       |
  |M|     |M|
   |       |
   6       5
​
*/
  pinMode(5, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(11, OUTPUT);
}
​
void loop()
{
  //前进
  //5→10
  digitalWrite(5,HIGH);
  digitalWrite(10,LOW);
   //6→11
  digitalWrite(6,HIGH);
  digitalWrite(11,LOW);
  delay(2000);
​
 //后退
  //10→5
  digitalWrite(5,LOW);
  digitalWrite(10,HIGH);
  //11→6
  digitalWrite(6,LOW);
  digitalWrite(11,HIGH);
  delay(2000);
​
  //停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  delay(2000);
​
  //前进
  //5→10
  digitalWrite(10,LOW);
  analogWrite(5,153);
     //6→11
  digitalWrite(11,LOW);
  analogWrite(6,153);
  delay(2000);
​
 //后退
  //10→5
  digitalWrite(5,LOW);
  analogWrite(10,153);
  //11→6
  digitalWrite(6,LOW);
  analogWrite(11,153);
  delay(2000);
​
  //停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  delay(2000);
 
}

 

Blinkgogo机器人套件初上手代码调试

教程一:简单介绍和电机驱动

//电机驱动01
 
/*
 
  11      10
   |       |
  |M|     |M|
   |       |
   6       5
 
*/
void setup()
{
  pinMode(11, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
}
 
void loop()
{
  // 左前进
  // 6↑ → 11↓
  digitalWrite(6,HIGH);
  digitalWrite(11,LOW);
  //延时1秒
  delay(1000);
  // 左后退
  // 11↑ → 6↓
  digitalWrite(6,LOW);
  digitalWrite(11,HIGH);
  //延时1秒
  delay(1000);
  // 左停止
  // 6↓ = 11↓
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  delay(1000);
 
  //右前进
  // 5↑ → 10↓
  digitalWrite(5,HIGH);
  digitalWrite(10,LOW);
  //延时1秒
  delay(1000);
  //右后退
  // 10↑ → 5↓
  digitalWrite(5,LOW);
  digitalWrite(10,HIGH);
  //延时1秒
  delay(1000);
  //右停止
  // 5↓ = 10↓
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  //延时1秒
  delay(1000);
}
//电机驱动02
 
/*
 
  11      10
   |       |
  |M|     |M|
   |       |
   6       5
 
*/
void setup()
{
  pinMode(11, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
}
 
void loop()
{
  //左前进 6↑ → 11↓
  digitalWrite(6,HIGH);
  digitalWrite(11,LOW);
  //右前进 5↑ → 10↓
  digitalWrite(5,HIGH);
  digitalWrite(10,LOW);
  delay(1000);
 
  //左停止
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  //右停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  delay(1000);
 
  //左后退 11↑ → 6↓
  digitalWrite(6,LOW);
  digitalWrite(11,HIGH);
  //右后退 10↑ → 5↓
  digitalWrite(5,LOW);
  digitalWrite(10,HIGH);
  delay(1000);
 
  //左停止
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  //右停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  delay(1000);
 
  //左转(左后退,右前进)
  digitalWrite(6,LOW);
  digitalWrite(11,HIGH);
  digitalWrite(5,HIGH);
  digitalWrite(10,LOW);
  delay(1000);
 
  //左停止
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  //右停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  delay(1000);
 
  //右转(左前进,右后退)
  digitalWrite(6,HIGH);
  digitalWrite(11,LOW);
  digitalWrite(5,LOW);
  digitalWrite(10,HIGH);
  delay(1000);
 
  //左停止
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  //右停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
  delay(1000);
}
//电机驱动03
 
int speed = 100;
 
void setup()
{
  pinMode(11, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
 
  Serial.begin(9600);
}
 
void loop()
{
  Serial.println("Forward");
  //左前进 6↑ → 11↓
  analogWrite(6,speed);
  digitalWrite(11,LOW);
  //右前进 5↑ → 10↓
  analogWrite(5,speed);
  digitalWrite(10,LOW);
  delay(1000);
 
  blinkgogoStop();
 
  Serial.println("Backward");
  //左后退 11↑ → 6↓
  digitalWrite(6,LOW);
  analogWrite(11,speed);
  //右后退 10↑ → 5↓
  digitalWrite(5,LOW);
  analogWrite(10,speed);
  delay(1000);
 
  blinkgogoStop();
 
  //左转(左后退,右前进)
  digitalWrite(6,LOW);
  analogWrite(11,speed);
  analogWrite(5,speed);
  digitalWrite(10,LOW);
  delay(1000);
 
  blinkgogoStop();
 
  //右转(左前进,右后退)
  analogWrite(6,speed);
  digitalWrite(11,LOW);
  digitalWrite(5,LOW);
  analogWrite(10,speed);
  delay(1000);
 
  blinkgogoStop();
  delay(1000);
}
 
 
void blinkgogoStop()
{
  //左停止
  digitalWrite(6,LOW);
  digitalWrite(11,LOW);
  //右停止
  digitalWrite(5,LOW);
  digitalWrite(10,LOW);
}

 

 

教程二:runBlinkgogo封装和走回形

 

//runBlinkgogo封装 测试
 
long speed;
 
/*
 
  11      10
   |       |
  |M|     |M|
   |       |
   6       5
 
*/
 
void runBlinkgogo(long leftSpeed, long rightSpeed)
{
  if (leftSpeed &gt;= 0)
  {
    //6→11
    analogWrite(6,leftSpeed);
    digitalWrite(11,LOW);
  }
  else
  {
    //11→6
    analogWrite(11,0-leftSpeed);
    digitalWrite(6,LOW);
 
  }
  if (rightSpeed &gt;= 0)
  {
    //5→10
    analogWrite(5,rightSpeed);
    digitalWrite(10,LOW);
  }
  else
  {
    //10→5
    analogWrite(10,0-rightSpeed);
    digitalWrite(5,LOW);
  }
}
 
void setup()
{
  speed = 200;
  pinMode(11, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
}
 
void loop()
{
  //前进
  runBlinkgogo(speed, speed);
  delay(1000);
  //后退
  runBlinkgogo(0 - speed, 0 - speed);
  delay(1000);
  //左转
  runBlinkgogo(0 - speed, speed);
  delay(1000);
  //右转
  runBlinkgogo(speed, 0 - speed);
  delay(1000);
  //停止
  runBlinkgogo(0, 0);
  delay(1000);
}
//
 
long speed;
 
/*
 
  11      10
   |       |
  |M|     |M|
   |       |
   6       5
 
*/
 
void runBlinkgogo(long leftSpeed, long rightSpeed)
{
  if (leftSpeed &gt;= 0)
  {
    //6→11
    analogWrite(6,leftSpeed);
    digitalWrite(11,LOW);
  }
  else
  {
    //11→6
    analogWrite(11,0-leftSpeed);
    digitalWrite(6,LOW);
 
  }
  if (rightSpeed &gt;= 0)
  {
    //5→10
    analogWrite(5,rightSpeed);
    digitalWrite(10,LOW);
  }
  else
  {
    //10→5
    analogWrite(10,0-rightSpeed);
    digitalWrite(5,LOW);
  }
}
 
void setup()
{
  speed = 200;
  pinMode(11, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
}
 
void loop()
{
  //前进
  runBlinkgogo(speed, speed);
  delay(1000);
 
  //停止
  runBlinkgogo(0, 0);
  delay(300);
 
  //右转
  runBlinkgogo(40, 0 - 40);
  delay(500);
 
  //停止
  runBlinkgogo(0, 0);
  delay(300);
}
//
 
long leftSpeed;
long rightSpeed;
 
/*
 
  11      10
   |       |
  |M|     |M|
   |       |
   6       5
 
*/
 
void runBlinkgogo(long leftSpeed, long rightSpeed)
{
  if (leftSpeed >= 0)
  {
    //6→11
    analogWrite(6, leftSpeed);
    digitalWrite(11, LOW);
  }
  else
  {
    //11→6
    analogWrite(11, 0 - leftSpeed);
    digitalWrite(6, LOW);
 
  }
  if (rightSpeed >= 0)
  {
    //5→10
    analogWrite(5, rightSpeed);
    digitalWrite(10, LOW);
  }
  else
  {
    //10→5
    analogWrite(10, 0 - rightSpeed);
    digitalWrite(5, LOW);
  }
}
 
void setup()
{
  pinMode(11, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  Serial.begin(9600);
}
 
void loop()
{
  // 顺时针转圈
  runBlinkgogo(255, 80);
  delay(8000);
  // 逆时针转圈
  runBlinkgogo(80, 255);
  delay(8000);
 
  // 操场式转圈
  runBlinkgogo(255, 100);
  delay(3600);
  runBlinkgogo(255, 255);
  delay(1000);
 
  // 8字型回环
  runBlinkgogo(255, 80);
  delay(4000);
  runBlinkgogo(255, 255);
  delay(1000);
 
  runBlinkgogo(80, 255);
  delay(4000);
  runBlinkgogo(255, 255);
  delay(1000);
 
 
  //蛇形前进(曲率变化)
  for (int speedOffset = -100; speedOffset < 100; speedOffset += 5)
  {
    leftSpeed = 125 + speedOffset;
    rightSpeed = 125 - speedOffset;
    Serial.print(leftSpeed);
    Serial.print("\t");
    Serial.println(rightSpeed);
    runBlinkgogo(leftSpeed, rightSpeed);
    delay(25);
  }
  delay(500); 
  for (int speedOffset = 100; speedOffset > -100; speedOffset -= 5)
  {
    leftSpeed = 125 + speedOffset;
    rightSpeed = 125 - speedOffset;
    Serial.print(leftSpeed);
    Serial.print("\t");
    Serial.println(rightSpeed);
    runBlinkgogo(leftSpeed, rightSpeed);
    delay(25);
  }
  delay(500);
}

教程三:探测黑线传感器使用和blink测试

 

//chapter 03 line_follow01
 
long runSpeed;        //电机基本速度
long sensorValueAll;  //存储左右巡线传感器L2、L3的检测值
byte lastDirection;   //存储上一次转向
 
void setup()
{
  runSpeed = 80;        //电机基本速度
  sensorValueAll = 0;   //存储左右巡线传感器L2、L3的检测值
  lastDirection = 0;    //存储上一次转向
  pinMode(2, INPUT);    //左巡线传感器L2
  pinMode(3, INPUT);    //右巡线传感器L3
  pinMode(11, OUTPUT);  //左马达 Motor B2
  pinMode(6, OUTPUT);   //左马达 Motor B1
  pinMode(10, OUTPUT);  //右马达 Motor A2
  pinMode(5, OUTPUT);   //右马达 Motor A1
}
 
void loop()
{
  //将L2与L3生成一个两位十进制数,存于sensorValueAll用于后续判断偏离方向分类
  sensorValueAll = 10 * digitalRead(2) + digitalRead(3);
  //用switch语句来选择分支机构,共4种情况
  switch (sensorValueAll)
  {
    case 0: //00,左右巡线传感器L2、L3都检测到黑线
      runBlinkgogo(runSpeed, runSpeed); //继续左右同速直线前行
      break;
    case 1: //01,左巡线传感器L2检测到黑线,右巡线传感器L3检测到离开黑线
      runBlinkgogo(runSpeed, 100);  //Motor B &lt; Motor A,左转 lastDirection = 0; //存储本次转向,零为右转 break; case 10: //10,左巡线传感器L2检测到离开黑线,右巡线传感器L3检测到黑线 runBlinkgogo(100, runSpeed); //Motor B &gt; Motor A,右转
      lastDirection = 1;  //存储本次转向,零为左转
      break;
    case 11:  //11,左右巡线传感器L2、L3都检测到离开黑线
      if (lastDirection)  //依照上次转向,单轮速度为零,加大转向速度
      {
        runBlinkgogo(runSpeed, 0);  //依照上次转向,右电机不动,左电机前进,右轮为中心右转
      }
      else
      {
        runBlinkgogo(0, runSpeed);  //依照上次转向,左电机不动,右电机前进,左轮为中心左转
      }
      break;
    default:  //其它情况
      runBlinkgogo(0, 0); //停机
      break;
  }
}
 
//传入左右电机运动参数,参数有效范围-256至于+255,
//但近0附近数值因摩擦阻力等存在死区,约-40至+40,导致电机无法转动
void runBlinkgogo(long leftSpeed, long rightSpeed)
{
  if (leftSpeed &gt;= 0)
  {
    //6→11
    analogWrite(6, leftSpeed);
    digitalWrite(11, LOW);
  }
  else
  {
    //11→6
    analogWrite(11, 0 - leftSpeed);
    digitalWrite(6, LOW);
 
  }
  if (rightSpeed &gt;= 0)
  {
    //5→10
    analogWrite(5, rightSpeed);
    digitalWrite(10, LOW);
  }
  else
  {
    //10→5
    analogWrite(10, 0 - rightSpeed);
    digitalWrite(5, LOW);
  }
}
//chapter 03 line_follow02
 
long speedBase = 100;   //电机基本速度
long speedOffset = 40;  //电机偏置速度
long speedLeft = 100;   //左电机速度参数传入值
long speedRight = 100;  //右电机速度参数传入值
boolean lastDirectionLeft = true; //上一次偏转方向是否为的右布尔值
 
 
void setup()
{
  pinMode(13, OUTPUT);
  pinMode(2, INPUT);
  pinMode(3, INPUT);
}
 
void loop()
{
  //根据左右巡线传感器L2、L3是否压线亮灯
  detectBlink();
  if(onLine(2)&amp;&amp;onLine(3))  //左右巡线传感器L2、L3都检测到黑线
  {
    runBlinkgogo(speedBase,speedBase);  //继续左右同速直线前行
  }
  if(!onLine(2)&amp;&amp;!onLine(3)) //左右巡线传感器L2、L3都检测到离开黑线 
  {
    if (lastDirectionLeft)
      runBlinkgogo(0, speedRight+speedOffset); //Motor B &lt; Motor A,左转 else runBlinkgogo(speedLeft+speedOffset, 0); //Motor B &gt; Motor A,右转
  }
  if(onLine(2)&amp;&amp;!onLine(3)) //左巡线传感器L2检测到黑线,右巡线传感器L3检测到离开黑线
  {
    runBlinkgogo(speedLeft-speedOffset, speedRight+speedOffset);
    lastDirectionLeft = true; //存储本次转向,零为右转
  }
  if(!onLine(2)&amp;&amp;onLine(3)) //左巡线传感器L2检测到离开黑线,右巡线传感器L3检测到黑线
  {
    runBlinkgogo(speedLeft+speedOffset, speedRight-speedOffset);
    lastDirectionLeft = false;  //存储本次转向,零为左转
  }
}
 
//检测Ln是否压黑线
boolean onLine(int Ln)
{
  return !digitalRead(Ln);
}
 
//检测到左右巡线传感器L2、L3都压黑线则关LED灯
//其中一个离开则亮全车LED灯提醒
void detectBlink()
{
  if(onLine(2) &amp;&amp; onLine(3))
    digitalWrite(13,LOW);
  else
    digitalWrite(13,HIGH);
}
 
//传入左右电机运动参数,参数有效范围-256至于+255,
//但近0附近数值因摩擦阻力等存在死区,约-40至+40,导致电机无法转动
void runBlinkgogo(long leftSpeed, long rightSpeed)
{
  if (leftSpeed &gt;= 0)
  {
    //6→11
    analogWrite(6,leftSpeed);
    digitalWrite(11,LOW);
  }
  else
  {
    //11→6
    analogWrite(11,0-leftSpeed);
    digitalWrite(6,LOW);
 
  }
  if (rightSpeed &gt;= 0)
  {
    //5→10
    analogWrite(5,rightSpeed);
    digitalWrite(10,LOW);
  }
  else
  {
    //10→5
    analogWrite(10,0-rightSpeed);
    digitalWrite(5,LOW);
  }
}
//chapter 03 line_follow03
 
#define L1 4  //左上角巡线传感器L1
#define L2 2  //左巡线传感器L2
#define L3 3  //右巡线传感器L3
#define L4 7  //右上角巡线传感器L4
 
long noLineTimes = 0;     //存储所有巡线传感器没检测到黑线的次数
long speedBase = 100;     //电机基本速度
long speedLeft;           //左电机速度参数传入值
long speedRight;          //右电机速度参数传入值
long speedOffset = 40;    //电机偏置速度
boolean lastDirectionLeft;//上一次偏转方向是否为右的布尔值
 
void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);    //全车LED
  pinMode(L1, INPUT);     //左上角巡线传感器L1
  pinMode(L2, INPUT);     //左巡线传感器L2
  pinMode(L3, INPUT);     //右巡线传感器L3
  pinMode(L4, INPUT);     //右上角巡线传感器L4
  speedLeft = speedBase;  //左电机速度参数传入值
  speedRight = speedBase; //右电机速度参数传入值
}
 
void loop()
{
  detectBlink();  //根据左右巡线传感器L2、L3是否压线亮灯
  //全部巡线传感器检测或检测不到黑线
  if ((onLine(L1) &amp;&amp; onLine(L2) &amp;&amp; onLine(L3) &amp;&amp; onLine(L4)) == true || (onLine(L1) || onLine(L2) || onLine(L3) || onLine(L4)) == false)
  {
    noLineTimes ++; //存储所有巡线传感器没检测到黑线的次数加一
    Serial.print("noLineTimes++");
    Serial.println(noLineTimes);
  }
  else
  {
    //L1压黑线
    if (onLine(L1))//L1 is connect to PIN D4
    {
      //只有L1压黑线
      if (!onLine(L2) &amp;&amp; !onLine(L3) &amp;&amp; !onLine(L4))
      {
        speedLeft = -speedOffset;
        speedRight = speedOffset;
        lastDirectionLeft = true;
      }
    }
    //L4黑线
    else if (onLine(L4))//L4 is connect to PIN D7
    {
      //只有L4压黑线
      if (!onLine(L1) &amp;&amp; !onLine(L2) &amp;&amp; !onLine(L3))
      {
        speedLeft = speedOffset;
        speedRight = -speedOffset;
        lastDirectionLeft = false;
      }
    }
    else
    {
      //左右巡线传感器L2、L3都检测到黑线
      if (onLine(L2) &amp;&amp; onLine(L3))
      {
        speedLeft = speedBase;
        speedRight = speedBase;
      }
      //L2压黑线、L3不压黑线
      if (onLine(L2) &amp;&amp; !onLine(L3))
      {
        speedLeft = speedBase;
        speedRight = speedBase + speedOffset;
        lastDirectionLeft = true;
      }
      //L2不压黑线、L3压黑线
      if (!onLine(L2) &amp;&amp; onLine(L3))
      {
        speedLeft = speedBase + speedOffset;
        speedRight = speedBase;
        lastDirectionLeft = false;
      }
      //L2与L3都不压黑线
      if (!onLine(L2) &amp;&amp; !onLine(L3))
      {
        //上一次转向为左
        if (lastDirectionLeft)
        {
          speedLeft = 0;
          speedRight = speedBase + speedOffset;
        }
        //上一次转向为右
        else
        {
          speedLeft = speedBase + speedOffset;
          speedRight = 0;
        }
      }
    }
    noLineTimes = 0;
  }
  //存储所有巡线传感器没检测到黑线的次数,是否传入电机参数运行
  if (noLineTimes &lt; 50) { runBlinkgogo(speedLeft, speedRight); } else { runBlinkgogo(0, 0); } } //检测Ln是否压黑线 boolean onLine(int Ln) { return !digitalRead(Ln); } void detectBlink() { if (onLine(2) &amp;&amp; onLine(3)) digitalWrite(13, LOW); else digitalWrite(13, HIGH); } //传入左右电机运动参数,参数有效范围-256至于+255, //但近0附近数值因摩擦阻力等存在死区,约-40至+40,导致电机无法转动 void runBlinkgogo(long leftSpeed, long rightSpeed) { if (leftSpeed &gt;= 0)
  {
    //6→11
    analogWrite(6, leftSpeed);
    digitalWrite(11, LOW);
  }
  else
  {
    //11→6
    analogWrite(11, 0 - leftSpeed);
    digitalWrite(6, LOW);
 
  }
  if (rightSpeed &gt;= 0)
  {
    //5→10
    analogWrite(5, rightSpeed);
    digitalWrite(10, LOW);
  }
  else
  {
    //10→5
    analogWrite(10, 0 - rightSpeed);
    digitalWrite(5, LOW);
  }
}

 

教程四:简单巡线和速度尝试

 

 

教程五:playMusic和速度参数调试lineFollower

 

 

教程六:寻光测试 lightFollower

 

 

教程七:走迷宫 goMaze

 

 

教程八:红外遥控控制原理和尝试测试

 

 

教程九:较大规模的程序功能划分与红外控制

 

 

教程十:集成手机应用程序的控制小车appControlBlinkgogo

 

 

W5200 以太网 扩展板 Arduino

欧美出口品质,国产价格,限时特价,限量促销,机不可失

原理图:http://i-element.org/makerstudio/W5200/Ethernet%20Shield%20v1.0%20sch.pdf

使用手册:http://i-element.org/makerstudio/W5200/MakerStudio%20Ethernet%20W5200%20Shield%20User%20Guide%20r1%20.pdf

Arduino使用范例代码:http://i-element.org/makerstudio/W5200/EthernetV1_0.zip

芯片相关参考手册:http://i-element.org/makerstudio/W5200/W5200_Datasheet.pdf

SIM900 GPRS GSM Arduino Shiled

欧美出口品质,国产价格,限时特价,限量促销,机不可失

 

原理图: http://i-element.org/makerstudio/GPRS/GPRS_GSM%20Shield%20v1.0%20sch.pdf

 

使用手册: http://i-element.org/makerstudio/GPRS/MakerStudio%20GPRS%20GSM%20Shield%20User%20Guide%20r1%20.pdf

 

Arduino使用范例代码: http://i-element.org/makerstudio/GPRS/GPRS_Shield.zip

 

芯片相关参考手册: http://i-element.org/makerstudio/GPRS/SIM900datasheeet.zip

 

Arduino寄存器操作

很多人都在向我询问关于如何直接使用Arduino中断的问题。要搞清楚这方面的事情,需要先了解Arduino使用的单片机ATmega328的内部寄存器。

要搞清楚这个问题,我们需要先获得一份ATmega328的Datasheet(数据手册),这是一款采用AVR架构的8位单片机。Datasheet是一份长达400多页的文档,告知了关于如何使用这款单片机的方方面面。

当我们使用PDF阅览器查看左侧的目录索引,我们先找到AVR CPU Core部分,查看概览Overview,我们能找到该款CPU的内部模块结构示意图。其中的ALU(算术和逻辑运算单元)部分,是用于算术和逻辑运算的核心部分,如加减乘除、位与或非运算等等。运算的数值来自23个8位的通用功能寄存器,也可将结果存放于这些寄存器中。8bit(8位)也就是1byte(一字节),还有其它的一些寄存器如Instruction Register指令寄存器等等。此外与8位Data Bus数据总线相连的还有I/O Module(输入/输出端口模块)。

你可以将Processer(处理器)想像成厨房,ALU(算术和逻辑运算单元)就像是大厨,根据instruction指令来准备晚餐。他是执行指令并采取响应的责任主体,大厨根据菜谱的步骤来开展工作,就像是编程过程中的程序代码。而厨房中的锅碗瓢盆等容器就是Register(寄存器),用于存放等等烹饪的食物,在处理器中也就是数据。

然而,当我将这些输入写入特殊功能寄存器SFR(sprcial function register)之中,它就可能直接导致控制器的某些引脚的电压从0V上升到5V。好,让我们待会就来试试。特殊功能寄存器SFR用来控制引脚的功能和输入输出,设置中断,发送或者接收串行信号等等。你需要知道的是,寄存器的种类数量以及名称是与处理器架构密切相关的,譬如你在ATMega328P控制器上做开发,当你迁移到ESP32控制器上时,你需要重新记忆一系列新的寄存器。

ATMega328P有三各分别编址的存储器,第一个是程序存储器(Program Memory),这里存储着你所编写的代码,当你编写代码,被编译后成为机器代码,就被存储在这里。这是一系列的操作指令,就像是菜谱一样,大厨操作时必须遵循的。第二个是SRAM(static random-access memory)静态随机存储器,有趣的是,我们上面所提及的通用寄存器和特殊功能寄存器都在这里的前256个存储单元中。所以,当我需要写入特殊功能寄存器数据时,我们需要先找到其在SRAM中的相应地址,然后配置好我们需要写入的数值。SRAM剩余的部分用于存储数值,如你在程序中所命名的变量等。请注意,当你的处理器重启或者断电后,SRAM中的所有数据将被清除,你将丢失所有存储于此的数据。最后第三个,就是EEPROM(elecrtically erasable programmable read-only memory)电可擦除只读存储器,它通常不被认为是处理器中的部分,由于其往往在另外的总线上与处理器进行通信,它的读取和写入速度也远较其它存储器慢。但它对于需要在断电后启动仍需保存使用的数据非常有用。

如果我们看一下I/O port章节部分,我们就可以知道一般数字输入输出端口的内部电气结构。在ATMega328P处理器中,所有I/O端口均可被配置为数字输入或输出端口,但是有些端口同时还可以被配置为通用串行收发端口、外部中断端口、模拟数字转换端口。

查看一下Pin Configurations章节,可以看一下DIP封装芯片的引脚分布,这就是Arduino UNO上所使用的处理器。你可以看到I/O端口命名以字母P开头,名字为B、C、D再加上一个端口数字。举例来说,我们可以向触发一个端口控制LED亮灭,该端口为PD5。需要使用一个引脚作为数字输入输出,我们需要配置3个寄存器PORT、DDR、PIN。让我们再导览到I/O Ports Register Description小节,了解更深入的信息。首先,我们需要配饰DDR(Data Direction Register)寄存器用于定义该端口的功能是输入R还是输出W。把DDRD中相应的端口位的值置位为1,意味着告诉处理器该位对应端口我们将用作输出功能,反正重置该位为0则告知处理器该对应端口将用作输入功能。接下来就是PORT寄存器,不要困惑,我们现在要操作的是D端口,那么对应的寄存器当然是PORTD,在前面配置了端口为输出功能后,将该寄存器对应位设置为0则该引脚输出0V电压,设置为1则该引脚输出5V电压。如果之前的DDR寄存器将该位对应端口设置为输入,则PORT寄存器对应位设置为1表示内部上拉电阻有效,设置为0表示内部上拉电阻无效。最后PIND寄存器相应位只能读取不能写入,它表示对应端口配置为输入时盖输入位引脚电平的高低。

我们待会就要实际演示 一下具体如何操作,不过,让我们先看看这些相关寄存器在SRAM中的分布位置和对应地址。导览到register summary一节,在表头我们可以看到每个寄存器的相应地址。

 

魔方破解机器人教程

拿到套件后,第一件事情就是清点一下零件数量是否有缺,部分消耗品或配件(如USB连接线、白乳胶、镊子)外观可能因不同批次有所差异,并不影响使用,部分易得选配件(如电池)可能不会附赠。

序号 名称 数量 功能 备注
1 木板A 1 搭建魔方破解机器人主体,支撑固定其他零件
2 木板B 1 搭建魔方破解机器人主体,支撑固定其他零件
3 木板C 1 搭建魔方破解机器人主体,支撑固定其他零件
4 木板D 1 搭建魔方破解机器人主体,支撑固定其他零件
5 木板E 1 搭建魔方破解机器人主体,支撑固定其他零件
6 扩展板 1 引出主控板连线,便于与舵机直接连接
7 主控板 1 接收电脑动作信号并转换为舵机运动指令
8 塑料舵机 1 用于带动旋转魔方基底的运动 与舵盘连接处白色
9 金属舵机 1 用于保持、释放和翻转魔方 与舵盘连接处黄色
10 电池盒 1 用于存放3节五号电池并给舵机供电 仅凭USB数据线来自电脑主板的供电无法使两个舵机正常运转,使用过程中
11 螺丝螺母收纳纸盒 1 收纳用于固定舵机和主控板与木板支架的螺丝和螺母
12 高质量魔方 1 高质量魔方 低质量魔方不能容忍旋转角度误差,旋转不到位,翻动到另一个面旋转时会卡住
13 USB数据线 1 用于给主控板烧录程序以及与电脑通信接受
14 五号电池 3 用于给舵机供电 不用依靠电脑供电,舵机会因供电不足而卡顿
15 十字螺丝刀 1 用于安装螺丝螺母和电池盒与拓展板之间的供电线连接
16 镊子 1 用于不便于手伸入的位置夹持和安装螺母,尤其是带尼龙圈的紧固螺母
17 白乳胶 1 没有热熔胶时使用用于木板间连接,但固化需要数小时之久,且不可加热拆卸,容易损坏木板 推荐使用热熔胶

 

结构拼装

魔方托盘

魔方卡手

主体安装

主体安装完成后,即可参考以下照片完成安装,有旋转部位的螺丝需要使用尼龙防松螺母,保证运转过程中不会因震动而松脱。

逐步安装过程拍照:

 

舵机接线参考

G V S

端口号

5

6

电池盒接线参考

VCC

电池盒红线

GND

电池盒黑线

打开设备管理器

在“我的电脑”或者计算机,右键它弹出菜单表,点击“管理”;(仅限于win7系统的用户)

在计算机管理页面左侧列表,可以找到“设备管理器”双击它,中间会显示硬件和设备是否正常的信息。有黄色感叹号就代表者有问题。

如果您的电脑买安装此驱动,需要参考以下网址,安装CH340驱动后,电脑即可与Arduino通讯

win7 http://www.arduined.eu/files/CH341SER.zip

win8 http://www.arduined.eu/files/windows8/CH341SER.zip

Mac http://kig.re/downloads/CH341SER_MAC.ZIP

Mac https://github.com/adrianmihalko/ch340g-ch34g-ch34x-mac-os-x-driver

点击[INSTALL],就可以完成驱动程序安裝。

确保驱动程序已经安装后,即可烧录Arduino程序

打开上述文件夹后

打开

程序运行

点击Upload

成功安装后,运行上位机

下列界面显示表示已经成功和Arduino通信

填上魔方颜色后,点击SOLVE,获得解答步骤后,点击SEND,即可

主控板烧录程序成功后,开机时魔方的基座旋转舵盘会归位,翻转手臂会自动翻转魔方一次。可视此过程为开机自检。如果此过程不正常,则需要重新剥离舵盘和舵机,以调整初始位置。

基座塑料舵机初始位:与翻转手轴线平行

翻转卡手舵机初始位:应远离魔方,卡在图中红色圈相应位置

 

如魔方运转过程中卡顿,请检查电池盒开关是否打开,电池盒与扩展板接线是否牢靠?

 

提示:上位机程序看上去魔方的正面是以翻动魔方手臂的方向看去为前方,即下图所示

程序调试部分支持信息提示:

 

先简单介绍一下舵机的基本知识。舵机简单的说就是集成了直流电机、电机控制器和减速器等,并封装在一个便于安装的外壳里的伺服单元。能够利用简单的输入信号比较精确的转动给定角度的电机系统。

舵机除电源外,只要一根信号线即可;使用PPM(脉冲比例调制)信号控制;所谓“PPM”,是一个周期约20ms,其间有个宽度在2ms 左右的脉冲控制信号。一般是以1.5ms 为基准,此时舵机居中,小于1.5ms 舵机左转,大于1.5ms,舵机右转;至于角度和脉冲宽度关系各个产品不同,例如:0.5ms 对应左转90 度,2.5ms 对应右转90 度。

 

由于使用不同的舵机采用的具体脉冲不同,即便是同一型号的舵机也因为死区不一致性,初始位置安装误差等,导致原始程序在部分用户安装的魔方破解机器人上不能正常工作。现根据我们所选用的舵机(塑料舵机:Futaba S3003、金属舵机:TowerPro MG995)上图图示情况进行简单的说明。

1ms(毫秒)=1000us(微秒),所以对应的情况是

500us =  0°

1000us = 45°

1500us = 90°

2000us = 135°

2500us = 180°

变化区间2000us内涵盖角度0-180°,由此可见脉宽每增加或减少约11us,舵机摆动角度相应改变1°。

想要了解更多知识,请参考http://www.i-element.org/servo/

 

现在我们来分析上下位机中对应的需要调整的程序参数。在控制板烧录完程序以后,每次上电启动,魔方破解机器人都会进行一次魔方翻转运动,由此时,你可以观察魔方是否被正确顺滑翻转。你也可以在上位机传送命令F(Flip)翻转,让机器人执行此指令,如下图所示

此外,上位机发送的全部指令一共就三种F(Flip)、H(Hold)、T(Turn)指令默认用空格隔开,对于一段魔方解答的结果就是由这三种指令的结果组成。(为了让部分有兴趣了解其算法的朋友能深入学习,后续我们将继续补充教程,针对此结果产生的算法进行讨论)目前我们仅讨论舵机参数需要调整以确保上诉3个动作能够完整的运行。

手臂舵机(armservo)总的来说有三个位置,对应3种指令

F(flip)翻转指令必须要保证手臂和魔方高度匹配,在中间黑色示意的导向区必须保证翻转动作后能自由落下到卡位,所以两块三角形导向板之间的连接螺丝必须保持适当的松紧度。

H(hold)把持指令执行时,手臂运动到此位置时,必须保证魔方的上两层被抓手握住,在魔方基底旋转时,魔方能被正确旋拧90度。

T(turn)旋转指令执行时,手臂必须退到导向板卡槽处附近,并向上倾斜约45度,手臂在此位置时,魔方基底旋转不会被手臂任何部位所阻碍。

接下来,我们看一下,在程序中,以上三个位置对应的参数

显然,

OFF对应T指令时的位置

HOLD对应H指令时的位置

PUSH对应F指令时的位置

你可以通过上述舵机的基本知识得到相应的关系去调整此3个参数,并重新烧录程序到Arduino控制板,获得手臂位置的微调。

需要注意的是,当上位机正在运行并与控制板通信时,烧录新程序将失败,因为两个程序竞争,只有能一个程序获得通过串口与控制板通信的权利。

也部分读者可能会问,为什么不是if-else语句中if下的3个参数,那是因为我们舵机可以正反安装,当反向安装时,上面的3个参数对应的位置才有效,否则默认执行else语句下的三个参数。

基底舵机(baseservo)总的来说有四个位置,分别在指令H和T中起作用

仔细观察的读者可能已经发现,放置魔方的基底托盘并不是刚好能放下魔方,而是比魔方略大,大概宽出3-4mm。这是多方面的原因导致的,但最直接的原因就是因为翻转魔方时需要空间,同时需要兼容导向板等可能产生的误差,这个道理同样适用于卡爪上,手臂最前端的卡爪也不能是恰恰好能卡住魔方而不多留一点剩余的空间。

这种容乃误差的做法,同样会给魔方扭转过程带入其它的新问题,如卡爪和底座都有空间,那么当卡爪把持魔方,底座完全旋转90°时,问题就出现了。由于空隙的存在,魔方往往不能恰好转到90°的位置。当一个位置扭不到准确的90°时,翻转或者旋转90°再扭时,魔方将会卡壳,甚至是损坏。由于我们选配了可以容错的高质量魔方(超过20元一个,而不是那种几块钱的魔方),这个问题其实已经可以容纳非常宽松的偏差,扭不到位的魔方在侧面再拧时会自动修复不到位的一面,目测10°以内不会有问题。即便如此,我们还是希望利用程序中的修正参数,把这个问题说清楚。

 

在魔方基底旋转的过程中,有原位和90°旋转位两个基本位置。几乎是同理加上了后缀词(over)。也就是说,当魔方基底选择时,先转到over位,然后再回到基本位,利用过转的这个超调量,克服每次动作时卡爪和基底的空隙带来的魔方90°旋转不到位。

基于上述的理论,你就会知道,当每次执行H(hold)指令后,如果魔方扭不到90°的正确位置,那么你就应该要尝试调整上面的4个参数咯。

学习,理解,锻炼,在充分思考后动手,我相信这次DIY的成功一定会磨炼出你的耐心和细心,创客精神与你我一同砥砺前行吧!