本系列文章的重点是基于可在所有机器人中重复使用的服务创建可扩展的面向对象的模块化软件架构,而无需在每次创建新机器人时都从头开始。
也许开始使用机器人技术的最具成本效益的方法是使用您可以在任何电子商务(全球速卖通、banggood 等)上购买的智能机器人汽车。但当然,购买它是最简单的部分……您不需要专门购买它,即使有了Smart Robot Car Yo的描述,您也会发现许多不同的变体。我将为您使用此套件获得的所有最常见模块创建“服务”,因此您可以选择您需要的服务,并将它们一起使用来创建您自己的智能机器人,而无需从头开始对每个机器人进行操作你做。
这是我将要创建的泰博机器人服务系列或文章的索引。
这种双路双向电机驱动器可让您轻松独立地控制两个双向最高 2A 的电机。
它是机器人应用的理想选择,非常适合连接到每个电机只需要几条控制线的任何 Arduino。
还集成了一个板载用户可访问的 5V 稳压器,可用于为需要高达约 1A 的稳压 5V 直流电源的任何其他电路供电。
我们将使用在上一篇文章中创建的基服务类。如果您没有阅读它,请阅读它,以便您了解我们将在这里创建的内容。
我们将创建另一个抽象层,它将定义任何 Motor Service 应具有的行为。这样做,允许我们为我们购买的任何硬件创建不同的电机服务,并以相同的方式使用我们拥有的任何电机服务(因此代码几乎没有变化)。
MotorService 类应该允许我们:
MotorService 类头文件 (MotorService.h) 如下所示:
#pragma once
#include "Arduino.h"
#include "Service.h"
namespace Taibot
{
class MotorService : public Service
{
public:
MotorService(bool isEnabled, bool isVerbose);
// Sets the speed of the motor
// speed = 0 then motor stops
// speed between -255 and -1 then motor runs backwards
// speed between 1 and 255 then motor runs forward
// Method that must be implemented in every MotorService specific implementation inheriting from the MotorService class
virtual void SetSpeed(int speed) = 0;
// Returns the current speed of the motor
// Method that must be implemented in every MotorService specific implementation inheriting from the MotorService class
virtual int GetSpeed() const = 0;
};
};
及其实现(MotorService.h):
#include "MotorService.h"
using namespace Taibot;
MotorService::MotorService(bool isEnabled, bool isVerbose) : Service(isEnabled, isVerbose)
{
}
如您所见,MotorService类除了为 MotorServices 实现定义契约外没有做任何其他事情(现在我们将处理L298N 电机驱动模块的实现)。
L298NMotorService类
最后我们得到了真正做某事的代码!
此类是MotorService的实现,它控制L298N 电机驱动模块。它遵循我们在前两个基类中指定的布局。
像往常一样,首先是头文件(L298NMotorService.h):
#pragma once
#include "MotorService.h"
namespace Taibot
{
class L298NMotorService : public MotorService
{
public:
L298NMotorService(bool isEnabled, bool isVerbose, unsigned int pinEnable, unsigned int pinIn1, unsigned int pinIn2);
// Implements the method inherited from the base MotorService class
void SetSpeed(int speed);
// Implements the method inherited from the base MotorService class
int GetSpeed() const;
void Setup();
void Update();
private:
unsigned int _pinEnable;
unsigned int _pinIn1;
unsigned int _pinIn2;
// Keeps track of the current speed of the Motor driver
unsigned int _currentSpeed = 0;
};
};
及其实现(L298NMotorService.cpp):
#include "L298NMotorService.h"
using namespace Taibot;
L298NMotorService::L298NMotorService(bool isEnabled, bool isVerbose, unsigned int pinEnable, unsigned int pinIn1, unsigned int pinIn2)
: MotorService(isEnabled, isVerbose)
{
_pinEnable = pinEnable;
_pinIn1 = pinIn1;
_pinIn2 = pinIn2;
_currentSpeed = 0;
}
void L298NMotorService::SetSpeed(int speed)
{
// Save the current speed...
_currentSpeed = speed;
if (IsVerbose())
{
//If we are logging, print the speed we are giving to the motor
Serial.print("L298NMotor: speed=");
Serial.println(speed);
}
// Only activate the motors if the driver is enabled
if (IsEnabled())
{
if (speed >= 0)
{
// if the speed is positive or 0 then move forward
analogWrite(_pinEnable, speed);
digitalWrite(_pinIn1, HIGH);
digitalWrite(_pinIn2, LOW);
}
else
{
// if the speed is negative then move backwards
analogWrite(_pinEnable, -speed);
digitalWrite(_pinIn1, LOW);
digitalWrite(_pinIn2, HIGH);
}
}
}
int L298NMotorService::GetSpeed() const
{
return _currentSpeed;
}
void L298NMotorService::Setup()
{
// We have nothing to set up
}
void L298NMotorService::Update()
{
// This service doesn't do anythin in background so this method is empty
}
真正的魔法发生在 SetSpeed 方法中。如您所见,在移动电机之前,我们正在检查服务是否已启用。此外,在打印调试信息之前,我们正在检查它是否为 Verbose。
此方法接收 -255 到 255 范围内的 int 值。
由于 L298N 模块的工作方式,我们应该向 _pinEnable 发送一个 PWM 脉冲,以设置旋转速度。这个脉冲可以在 0 到 255 之间。我们正在使用analogWrite调用来设置 Arduino 上数字引脚的 PWM 输出。(这里有更多详细信息)。
我们还将_pinIn1和_pinIn2设置为 HIGH 或 LOW,具体取决于旋转方向,如 L298N 模块的文档所述。
我们现在需要将代码添加到草图以测试此服务。我认为您可能会理解为什么我们一直在处理所有这些代码混乱。
这是 Arduino Sketch 的外观:
/*
Name: Taibot.ino
Created: 12/13/2016 10:27:53 AM
Author: Nahuel Taibo savagemakers.com
*/
#include "L298NMotorService.h"
using namespace Taibot;
// Pin definitions for the L298N Motor driver (Change this defines according to your hardware configuration)
#define PIN_L298N_ENA PIN2
#define PIN_L298N_IN1 PIN3
#define PIN_L298N_IN2 PIN4
#define PIN_L298N_IN3 PIN5
#define PIN_L298N_IN4 PIN6
#define PIN_L298N_ENB PIN7
L298NMotorService rightMotor(true, true, PIN_L298N_ENA, PIN_L298N_IN1, PIN_L298N_IN2);
L298NMotorService leftMotor(true, true, PIN_L298N_ENB, PIN_L298N_IN3, PIN_L298N_IN4);
//We will use this variables to change the robot speed on after some seconds (without using delays)
unsigned long previousTime = millis();
unsigned int updateFreq = 5000;
// the setup function runs once when you press reset or power the board
void setup()
{
Serial.begin(115200);
// Call the Setup method of all the services we are using
rightMotor.Setup();
leftMotor.Setup();
Serial.println("Taibot Started.");
}
// the loop function runs over and over again until power down or reset
void loop()
{
// Call the Update method of all the service we are using...
rightMotor.Update();
leftMotor.Update();
// Do anything else we want to do...
if ((previousTime + updateFreq) < millis())
{
previousTime = millis();
if (rightMotor.GetSpeed() > 0)
{
rightMotor.SetSpeed(0);
leftMotor.SetSpeed(0);
}
else
{
rightMotor.SetSpeed(150);
leftMotor.SetSpeed(150);
}
}
}
如果一切正常,构建此代码并将其上传到您的机器人后,您应该会看到它前进 5 秒,停止 5 秒,并重复该行为,直到您将其关闭。
是的,它有很多代码只是为了推动机器人前进,但是一旦我们开始添加越来越多的服务,您就会注意到这些代码比无休止的意大利面条代码更有意义。我希望您能关注本系列,因为我们会在每一篇新文章中不断为您的机器人提供越来越多的智能。
您将找到本文附带的代码存储库。我创建了一个仅包含此服务实现的分支,因此如果您有意的话,您只能获得此服务。
如果您不理解本文的任何部分、无法使此代码工作、发现错误或有任何建议,请告诉我,我愿意改进它并使其成为任何人的干净代码库想要重用它。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !