制作一个串联仿生毛毛虫【内附资料下载链接】

1. 运动功能说明

       串联仿生毛毛虫样机是一款纯关节串联的蠕虫形机器人,可以像虫子一样的上下蠕动前进,样子呈波浪状。

2. 结构说明

       该样机由5个关节模组串联构成,结构比较单纯。

3. 运动功能实现

本样机只需要让5个舵机按照一定的时序,做出波浪形摆动的动作,即可实现蠕动爬行。

舵机和关节模组的控制方法请参考【U002】如何驱动模拟舵机,以及【M002】舵机关节模组

3.1 电子硬件

在这个示例中,采用了以下硬件,请大家参考:

Basra主控板(基于Arduino开源方案设计的一款开发板)、Bigfish扩展板、7.4V锂电池

【Basra主控板的相关介绍可点击查看https://www.robotway.com/h-col-135.html;Bigfish扩展板的相关介绍可点击查看https://www.robotway.com/h-col-136.html

3.2 编写程序

编程环境:Arduino 1.8.19

(1)实现尾部关节运动(Tail_Swing.ino),代码如下:

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2022 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2022-9-14 https://www.robotway.com/

  ------------------------------

  实验功能: 实现机器毛毛虫尾部关节运动。

  -----------------------------------------------------

  实验接线:尾部关节模组的舵机接D8。                                     

------------------------------------------------------------------------------------*/

#include <Servo.h>

#include <MsTimer2.h>

int servoPort[5]={8,3,11,7,4}; //毛毛虫的舵机连接顺序及接口号,从尾巴开始。

Servo myServo[5];

long timeCount;

#define DELTATIME 10

void setup() {         

  Serial.begin(9600);       

  for(int i = 0; i < 5; i++)

    myServo[i].attach(servoPort[i]);   

  MsTimer2::set(DELTATIME, Timer);

  MsTimer2::start();

  delay(100);

}

void loop() {

  ServoMove(0,60,120,2000); //0号舵机在2000ms内从60°转到120°

}

void Timer() {

  timeCount++;

}

void ServoMove(int which, int start, int finish, long t)

{

  static int a;

  static long count = 0;

  static int i = 0;

  static boolean begin = true;

  if(begin){

    if( ( start - finish ) > 0 )

      a = -1;

    else

      a = 1;

    count = timeCount;

    begin = false;

  }

  else{

    if( ( timeCount - count ) < (t/DELTATIME) ){

      if( ( timeCount - count ) > ( i * (t/DELTATIME) / (abs(start-finish)) ) ){

        myServo[which].write( start + i * a );  

        delay(1);

        i++;     

        Serial.println(start + i * a);

      }

    }

    else{

      i = 0;

      begin = true;

      count = 0;

    }   

  }

}

       此段程序之所以看起来那么复杂,是因为它搭建了一个程序框架,在这个框架中,大家可以通过改变参数,来决定哪个舵机运动,以及怎么运动,不需要重写整个程序。

      (2)编写并烧录以下程序,它可以让毛毛虫全身的关节一起运动(Body_Swing.ino),代码如下:

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2022 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2022-9-14 https://www.robotway.com/

  ------------------------------

  实验功能: 实现机器毛毛虫全身关节运动。

  -----------------------------------------------------

  实验接线:从尾巴开始,对应舵机接口依次D8、D3、D11、D7、D4。                                     

------------------------------------------------------------------------------------*/

#include <Servo.h>

#include <MsTimer2.h>

int servoPort[5]={8,3,11,7,4};

Servo myServo[5];

long servoCount[5];

boolean begin[5] = {true,true,true,true,true};

boolean complete[5] = {false,false,false,false,false};

int direct[5] = {1,1,1,1,1};

int delta[5] = {0,0,0,0,0};

long timeCount;

#define DELTATIME 10

int up = 30; //关节起始角度

int down = 120; //关节终止角度

int turnTime = 1000; //关节运动时间

String inputString = "";         // a string to hold incoming data

boolean stringComplete = false;   // whether the string is complete

void setup() {         

  Serial.begin(9600);       

  for(int i = 0; i < 5; i++)

    myServo[i].attach(servoPort[i]);   

  MsTimer2::set(DELTATIME, Timer);

  MsTimer2::start();

  delay(100);

}

void loop() {

  static int phase[5] = {0,0,0,0,0};

  if (stringComplete) {

    Serial.println(inputString);

    up = inputString.substring(0,inputString.indexOf(',')).toInt();

    inputString = inputString.substring(inputString.indexOf(',')+1,inputString.length());

    down = inputString.substring(0,inputString.indexOf(',')).toInt();

    turnTime = inputString.substring(inputString.indexOf(',')+1,inputString.length()).toInt();

    Serial.println(up);

    Serial.println(down);

    Serial.println(turnTime);

    inputString = "";

    stringComplete = false;

  }

  if(phase[0] == 0)

    if(ServoMove(0,up,down,turnTime))

      phase[0] = 1;

  if(phase[0] == 1)

    if(ServoMove(0,down,up,turnTime))

      phase[0] = 0;

  if(phase[1] == 0)

    if(ServoMove(1,down,up,turnTime))

      phase[1] = 1;

  if(phase[1] == 1)

    if(ServoMove(1,up,down,turnTime))

      phase[1] = 0;

  if(phase[2] == 0)

    if(ServoMove(2,up,down,turnTime))

      phase[2] = 1;

  if(phase[2] == 1)

    if(ServoMove(2,down,up,turnTime))

      phase[2] = 0;

  if(phase[3] == 0)

    if(ServoMove(3,down,up,turnTime))

      phase[3] = 1;

  if(phase[3] == 1)

    if(ServoMove(3,up,down,turnTime))

      phase[3] = 0;

  if(phase[4] == 0)

    if(ServoMove(4,up,down,turnTime))

      phase[4] = 1;

  if(phase[4] == 1)

    if(ServoMove(4,down,up,turnTime))

      phase[4] = 0;

}

void Timer() {

  timeCount++;

}

void serialEvent() {

  while (Serial.available()) {

    char inChar = (char)Serial.read();

    inputString += inChar;

      if (inChar == '\n')

      stringComplete = true;

    }

}

boolean ServoMove(int which, int start, int finish, long t)

{

  if(begin[which]){

    if( ( start - finish ) > 0 )

      direct[which] = -1;

    else

      direct[which] = 1;

    servoCount[which] = timeCount;

    begin[which] = false;

    complete[which] = false;

  }

  else{

    if( ( timeCount - servoCount[which] ) < (t/DELTATIME) ){

      if( ( timeCount - servoCount[which] ) > ( delta[which] * (t/DELTATIME) / (abs(start-finish)) ) ){

        myServo[which].write( start + delta[which] * direct[which] );

        delay(1);

        delta[which]++;     

        //Serial.println(start + i * a);

      }

    }

    else{

      delta[which] = 0;

      begin[which] = true;

      servoCount[which] = 0;

      complete[which] = true;

    }   

  }

  return (complete[which]);

}

       烧录后,打开Serial Monitor,输入up,down,turnTime的值,可直接改变全部关节的运动参数,例如,输入50,100,2000。

       关节的运动状态会实时发生变化。其中50代表摆动的起始角度50°,100代表摆动的终止角度100°,2000代表运动持续时间是2000毫秒。

      (3) 上一例程中,机器毛毛虫相邻关节的运动相位差为180°,根据样机的运动姿态分析,各个关节的运动相位差为90°左右的时候运动效应该比较好。

       将相邻关节的运动相位差调整为90°(Worm_Go.ino),代码如下:

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2022 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2022-9-14 https://www.robotway.com/

  ------------------------------

  实验功能: 实现机器毛毛虫向前蠕动爬行。

  -----------------------------------------------------

  实验接线:从尾巴开始,对应舵机接口依次D8、D3、D11、D7、D4。                                     

------------------------------------------------------------------------------------*/

#include <Servo.h>

#include <MsTimer2.h>

int servoPort[5]={8,3,11,7,4};

Servo myServo[5];

long servoCount[5];

boolean begin[5] = {true,true,true,true,true};

boolean complete[5] = {false,false,false,false,false};

int direct[5] = {1,1,1,1,1};

int delta[5] = {0,0,0,0,0};

long timeCount;

#define DELTATIME 10

int up = 30;

int down = 120;

int turnTime = 1000;

String inputString = "";         // a string to hold incoming data

boolean stringComplete = false;   // whether the string is complete

void setup() {         

  Serial.begin(9600);       

  for(int i = 0; i < 5; i++)

  myServo[i].attach(servoPort[i]);   

  MsTimer2::set(DELTATIME, Timer);

  MsTimer2::start();

  delay(100);

}

void loop() {

static int phase[5] = {0,0,0,0,0};

if (stringComplete) {

Serial.println(inputString);

up = inputString.substring(0,inputString.indexOf(',')).toInt();

inputString = inputString.substring(inputString.indexOf(',')+1,inputString.length());

down = inputString.substring(0,inputString.indexOf(',')).toInt();

turnTime = inputString.substring(inputString.indexOf(',')+1,inputString.length()).toInt();

Serial.println(up);

Serial.println(down);

Serial.println(turnTime);

inputString = "";

stringComplete = false;

}

if(phase[0] == 0)

if(ServoMove(0,up,down,turnTime))

phase[0] = 1;

if(phase[0] == 1)

if(ServoMove(0,down,up,turnTime))

phase[0] = 0;

if(servoCount[0]>0 && (timeCount - servoCount[0])>(turnTime/(4*DELTATIME))){

if(phase[1] == 0)

if(ServoMove(1,up,down,turnTime))

phase[1] = 1;

if(phase[1] == 1)

if(ServoMove(1,down,up,turnTime))

phase[1] = 0;

}

if(servoCount[1]>0 && (timeCount - servoCount[1])>(turnTime/(4*DELTATIME))){

if(phase[2] == 0)

if(ServoMove(2,up,down,turnTime))

phase[2] = 1;

if(phase[2] == 1)

if(ServoMove(2,down,up,turnTime))

phase[2] = 0;

}

if(servoCount[2]>0 && (timeCount - servoCount[2])>(turnTime/(4*DELTATIME))){

if(phase[3] == 0)

if(ServoMove(3,up,down,turnTime))

phase[3] = 1;

if(phase[3] == 1)

if(ServoMove(3,down,up,turnTime))

phase[3] = 0;

}

if(servoCount[3]>0 && (timeCount - servoCount[3])>(turnTime/(4*DELTATIME))){

if(phase[4] == 0)

if(ServoMove(4,up,down,turnTime))

phase[4] = 1;

if(phase[4] == 1)

if(ServoMove(4,down,up,turnTime))

phase[4] = 0;

}

}

void Timer() {

timeCount++;

}

void serialEvent() {

while (Serial.available()) {

char inChar = (char)Serial.read();

inputString += inChar;

    if (inChar == '\n')

stringComplete = true;

  }

}

boolean ServoMove(int which, int start, int finish, long t)

{

if(begin[which]){

if( ( start - finish ) > 0 )

direct[which] = -1;

else

direct[which] = 1;

servoCount[which] = timeCount;

begin[which] = false;

complete[which] = false;

}

else{

if( ( timeCount - servoCount[which] ) < (t/DELTATIME) ){

if( ( timeCount - servoCount[which] ) > ( delta[which] * (t/DELTATIME) / (abs(start-finish)) ) ){

myServo[which].write( start + delta[which] * direct[which] );

delay(1);

delta[which]++;

//Serial.println(start + i * a);

}

}

else{

delta[which] = 0;

begin[which] = true;

//servoCount[which] = 0;

complete[which] = true;

}

}

return (complete[which]);

}

       烧录后,打开Serial Monitor,输入up,down,turnTime的值,并实时观察运动变化。你可以通过这种方法调整机器人的运动姿态,直至对爬行效果满意为止。

4. 扩展样机

       本样机没有设计左右摆动的结构,因此不能左右转向,有兴趣的朋友可以尝试为其加上左右摆动的结构,让它的运动方式更加丰富。

5. 资料下载

资料内容:例程源代码、样机3D文件

下载途径:串联仿生毛毛虫

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值