Tão importante quanto os circuitos e a montagem, é o software que irá controlar os movimentos do robô. A unit ParBib descrita no capítulo 4, foi criada apenas para enviar sinais à porta paralela, mas para que esses sinais se transformem em movimento no robô, eles precisam ser gerados com coerência e sincronia, sendo essa a função do software de navegação.
Para gerar os sinais de navegação foi desenvolvida uma classe que fornece os recursos básicos de movimentação do robô. A classe TRobotMove declarada dentro da unit RobotMove, permite selecionar entre 9 tipos de movimento, alterar a velocidade e ligar/desligar o modo de alto torque.
Métodos públicos da classe TRobotMove
Abaixo segue a declaração da parte pública da classe e uma breve explicação sobre o uso de cada método. O tópico seguinte contém a listagem completa da classe.
type
TRobotMove = class
public
constructor Create(AOwner: TComponent;
LPTPort: TLPT;
BaseTm: cardinal);
destructor Destroy; override;
procedure Ahead;
procedure Back;
procedure Stop;
procedure Right;
procedure Left;
procedure VeryRight;
procedure VeryLeft;
procedure SpinRight;
procedure SpinLeft;
property PulseTime: cardinal;
property HiTorq: boolean;
end;
constructor
Create(AOwner: TComponent; LPTPort: TLPT;
BaseTm: cardinal);
Construtor da classe. Usa os seguintes parâmetros:
AOwner: Pode ser qualquer componente
descendente de TComponent. Normalmente é o próprio form principal da aplicação.
Este parâmetro é necessário porque a classe usa internamente um timer para
gerar os pulsos e a construção do timer exige um Owner.
LPTPort:
Porta paralela que será usada. Em geral é a LPT1.
BaseTM: Tempo base para o timer em milissegundos. Esse
é o tempo que será usado entre cada pulso enviado para os motores. Quanto menor
o tempo, mais rápido o robô vai se mover. Existe um valor mínimo para esse
tempo que depende do tipo do motor de passo e do peso do robô. Caso sejam
usados valores abaixo do mínimo, os motores começam a perder passo ou podem nem
se mover.
destructor Destroy;
Destrutor da classe.
procedure Ahead;
Move para frente em linha reta.
procedure Back;
Move para trás em linha reta.
procedure Stop;
Para o movimento, mas as bobinas continuam ativadas. Isso
cria um efeito de “freio de mão” no robô.
procedure Right;
Curva leve à direita. A roda da direita passa a rodar com a
metade da velocidade da roda da esquerda.
procedure Left;
Curva leve à esquerda. Aqui é a roda da esquerda que tem sua
velocidade reduzida pela metade.
procedure VeryRight;
Curva fechada à direita. A roda da direita fica travada na
mesma posição e a roda da esquerda continua no movimento normal. O efeito disso
é que o robô começa a circular em torno da roda da direita.
procedure VeryLeft;
Curva fechada à esquerda. Idem acima.
procedure SpinRight;
Giro à direita. A roda da direita tem o movimento invertido e
passa a rodar para trás, enquanto a roda da esquerda se move para frente. O
efeito é que o robô começa a girar no sentido horário sem sair do lugar.
procedure SpinLeft;
Giro à esquerda. A roda da esquerda inverte o movimento e o
robô gira no sentido anti-horário.
property PulseTime: cardinal;
Permite ler e alterar o tempo (em milissegundos) entre cada
pulso durante o movimento.
property HiTorq: boolean;
Liga/desliga o modo de alto torque.
Listagem da classe TRobotMove
unit RobotMove;
interface
uses
SysUtils, Classes, DateUtils, Forms, ExtCtrls,
ParBib;
type
TRobotMove = class
private
lport: TLPT;
tm: TTimer;
FHiTorq:
boolean; // Ativa/desativa alto
torque
dvLeft, dvRight, // divisor de frequencia
psLeft, psRight: byte; // pulsos
fwLeft, fwRight: boolean; // direção
onLeft, onRight: boolean; // anda / para
baseCount: integer; // contador base
FBaseTime: cardinal;
procedure OnTimer(Sender: TObject);
public
constructor Create(AOwner: TComponent;
LPTPort: TLPT;
BaseTm: cardinal);
destructor Destroy; override;
procedure Ahead;
procedure Back;
procedure Stop;
procedure Right;
procedure Left;
procedure VeryLeft;
procedure VeryRight;
procedure SpinLeft;
procedure SpinRight;
property PulseTime: cardinal read FBaseTime
write FBaseTime;
property HiTorq: boolean read FHiTorq write
FHiTorq;
end;
implementation
{
TRobotMove }
constructor
TRobotMove.Create(AOwner: TComponent; LPTPort: TLPT;
BaseTm:
cardinal);
begin
inherited Create;
lport:= LPTPort;
SetLPT(lport);
tm:= TTimer.Create(AOwner);
tm.Enabled:= false;
FBaseTime := BaseTm;
tm.Interval:= FBaseTime;
tm.OnTimer:= OnTimer;
dvLeft:= 1;
dvRight:= 1;
psLeft:= 1;
psRight:= 1;
fwLeft:= true;
fwRight:= true;
baseCount := 1;
onLeft:= false;
onRight:= false;
tm.Enabled:= true;
FHiTorq := false;
end;
procedure
TRobotMove.OnTimer(Sender: TObject);
var pr,
pl: byte;
begin
if (baseCount mod dvRight = 0) and onRight
then
begin
if fwRight then
begin
if psRight = 8
then psRight := 1
else psRight := psRight shl 1;
end
else begin
if psRight = 1
then psRight := 8
else psRight := psRight shr 1;
end;
end;
if (baseCount mod dvLeft = 0) and onLeft then
begin
if fwLeft then
begin
if psLeft = 1
then psLeft := 8
else psLeft := psLeft shr 1;
end
else begin
if psLeft = 8
then psLeft := 1
else psLeft := psLeft shl 1;
end;
end;
if FHiTorq then
begin
pr := psRight or (psRight shl 1);
if pr >= 16 then pr := psRight + 1;
pl := psLeft or (psLeft shl 1);
if pl >= 16 then pl := psLeft + 1;
SetDataByte((pl shl 4) or pr);
end
else SetDataByte((psLeft shl 4) or psRight);
Inc(baseCount);
baseCount := baseCount mod 100000;
tm.Interval:= FBaseTime;
end;
procedure
TRobotMove.Ahead;
begin
onLeft:= true;
onRight:= true;
fwLeft:= true;
fwRight:= true;
dvLeft:= 1;
dvRight:= 1;
end;
procedure
TRobotMove.Back;
begin
onLeft:= true;
onRight:= true;
fwLeft:= false;
fwRight:= false;
dvLeft:= 1;
dvRight:= 1;
end;
procedure
TRobotMove.Stop;
begin
onLeft:= false;
onRight:= false;
end;
procedure
TRobotMove.Right;
begin
onLeft:= true;
onRight:= true;
fwLeft:= true;
fwRight:= true;
dvLeft:= 1;
dvRight:= 2;
end;
procedure
TRobotMove.Left;
begin
onLeft:= true;
onRight:= true;
fwLeft:= true;
fwRight:= true;
dvLeft:= 2;
dvRight:= 1;
end;
procedure
TRobotMove.VeryRight;
begin
onLeft:= true;
onRight:= false;
fwLeft:= true;
fwRight:= true;
dvLeft:= 1;
dvRight:= 1;
end;
procedure
TRobotMove.VeryLeft;
begin
onLeft:= false;
onRight:= true;
fwLeft:= true;
fwRight:= true;
dvLeft:= 1;
dvRight:= 1;
end;
procedure
TRobotMove.SpinRight;
begin
onLeft:= true;
onRight:= true;
fwLeft:= true;
fwRight:= false;
dvLeft:= 1;
dvRight:= 1;
end;
procedure
TRobotMove.SpinLeft;
begin
onLeft:= true;
onRight:= true;
fwLeft:= false;
fwRight:= true;
dvLeft:= 1;
dvRight:= 1;
end;
destructor
TRobotMove.Destroy;
var tmp:
TDateTime;
begin
tm.OnTimer:= nil;
tm.Enabled:= false;
tmp := IncMilliSecond(Now, tm.Interval+2);
while tmp > Now do
Application.ProcessMessages;
tm.Free;
SetDataByte(0);
end;
end.
Programa de navegação e controle do robô
O programa listado a seguir usa a classe TRobotMove para
controlar o robô e fornece uma interface para o usuário. Basicamente o programa
associa um botão na tela de interface a cada método fornecido pela classe. As
funções de movimento também podem ser ativadas pelo teclado numérico. O tempo
mostrado no canto inferior direito do form é o intervalo de tempo entre cada
pulso enviado aos motores. Diminuir o tempo aumenta a velocidade do robô, mas
existe um limite que varia de acordo com os motores utilizados e o peso do
robô. Se usado um intervalo abaixo desse limite, os motores começam a perder
passos ou simplesmente param. A figura
33 mostra o form da aplicação.
As imagens que ilustram os botões foram omitidas da listagem
para que a mesma não fique excessivamente longa. Como se trata de um form, foi
incluído o arquivo dfm e o arquivo pas, além do arquivo de projeto.
Figura 33: Tela do programa de navegação |
Arquivo de projeto RoboNav.dpr
program RoboNav;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
RobotMove in 'RobotMove.pas',
ParBib in 'ParBib.pas';
{$R
*.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Definição do form de interface, arquivo Unit1.dfm
object
Form1: TForm1
Left =
269
Top = 108
Width = 528
Height = 291
Caption = 'Robô Nav'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = [fsBold]
OldCreateOrder = False
OnCreate = FormCreate
OnKeyPress = FormKeyPress
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 16
Top = 11
Width = 64
Height = 13
Caption = 'Portas LPT'
end
object btOn: TButton
Left = 13
Top = 60
Width = 112
Height = 57
Caption = 'Ativar'
TabOrder = 0
OnClick = btOnClick
OnKeyPress = FormKeyPress
end
object btOff: TButton
Left = 13
Top = 124
Width = 112
Height = 57
Caption = 'Desligar'
Enabled = False
TabOrder = 1
OnClick = btOffClick
OnKeyPress = FormKeyPress
end
object cbxPortas: TComboBox
Left = 16
Top = 32
Width = 107
Height = 21
ItemHeight = 13
ItemIndex = 0
TabOrder = 2
Text = 'LPT1'
OnKeyPress = FormKeyPress
Items.Strings = (
'LPT1'
'LPT2')
end
object pnMov: TPanel
Left = 138
Top = 32
Width = 367
Height = 222
BevelOuter = bvNone
Enabled = False
TabOrder = 3
object Label2: TLabel
Left = 264
Top = 136
Width = 66
Height = 13
Caption = 'Tempo (ms)'
end
object btLeft: TBitBtn
Left = 7
Top = 6
Width = 68
Height = 66
TabOrder = 0
OnClick = btLeftClick
OnKeyPress = FormKeyPress
end
object btAhead: TBitBtn
Left = 79
Top = 6
Width = 68
Height = 66
TabOrder = 1
OnClick = btAheadClick
OnKeyPress = FormKeyPress
end
object btRight: TBitBtn
Left = 151
Top = 6
Width = 68
Height = 66
TabOrder = 2
OnClick = btRightClick
OnKeyPress = FormKeyPress
end
object btVRight: TBitBtn
Left = 151
Top = 77
Width = 68
Height = 66
TabOrder = 3
OnClick = btVRightClick
OnKeyPress = FormKeyPress
end
object btStop: TBitBtn
Left = 79
Top = 77
Width = 68
Height = 66
TabOrder = 4
OnClick = btStopClick
OnKeyPress = FormKeyPress
end
object btVLeft: TBitBtn
Left = 7
Top = 77
Width = 68
Height = 66
TabOrder = 5
OnClick = btVLeftClick
OnKeyPress = FormKeyPress
end
object btSLeft: TBitBtn
Left = 7
Top = 148
Width = 68
Height = 66
TabOrder = 6
OnClick = btSLeftClick
OnKeyPress = FormKeyPress
end
object btBack: TBitBtn
Left = 79
Top = 148
Width = 68
Height = 66
TabOrder = 7
OnClick = btBackClick
OnKeyPress = FormKeyPress
end
object btSRight: TBitBtn
Left = 151
Top = 148
Width = 68
Height = 66
TabOrder = 8
OnClick = btSRightClick
OnKeyPress = FormKeyPress
end
object btTurbo: TBitBtn
Left = 249
Top = 6
Width = 113
Height = 105
Caption = 'Turbo'
TabOrder = 9
OnClick = btTurboClick
Layout = blGlyphTop
end
object seTime: TSpinEdit
Left = 262
Top = 152
Width = 97
Height = 22
Enabled = False
MaxValue = 100
MinValue = 10
TabOrder = 10
Value = 35
OnChange = seTimeChange
end
end
end
Listagem do arquivo Unit1.pas
unit Unit1;
interface
uses
Forms, StdCtrls, Spin, Buttons, Controls,
ExtCtrls, Classes,
ParBib, RobotMove;
type
TForm1 = class(TForm)
btOn: TButton;
btOff: TButton;
Label1: TLabel;
cbxPortas: TComboBox;
pnMov: TPanel;
btLeft: TBitBtn;
btAhead: TBitBtn;
btRight: TBitBtn;
btVRight: TBitBtn;
btStop: TBitBtn;
btVLeft: TBitBtn;
btSLeft: TBitBtn;
btBack: TBitBtn;
btSRight: TBitBtn;
btTurbo: TBitBtn;
Label2: TLabel;
seTime: TSpinEdit;
procedure btOnClick(Sender: TObject);
procedure btOffClick(Sender: TObject);
procedure btTurboClick(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var
Key: Char);
procedure FormCreate(Sender: TObject);
procedure btLeftClick(Sender: TObject);
procedure btVLeftClick(Sender: TObject);
procedure btSLeftClick(Sender: TObject);
procedure btAheadClick(Sender: TObject);
procedure btStopClick(Sender: TObject);
procedure btBackClick(Sender: TObject);
procedure btRightClick(Sender: TObject);
procedure btVRightClick(Sender: TObject);
procedure btSRightClick(Sender: TObject);
procedure seTimeChange(Sender: TObject);
private
function LPTSelected: TLPT;
public
rob: TRobotMove;
end;
var
Form1: TForm1;
implementation
{$R
*.dfm}
{ TForm1
}
function
TForm1.LPTSelected: TLPT;
begin
case cbxPortas.ItemIndex of
0: result := pLPT1;
else result := pLPT2;
end;
end;
procedure
TForm1.btOnClick(Sender: TObject);
begin
rob:= TRobotMove.Create(self, LPTSelected,
seTime.Value);
btOff.Enabled:= true;
btOn.Enabled:= false;
pnMov.Enabled:= true;
end;
procedure
TForm1.btOffClick(Sender: TObject);
begin
rob.Stop;
rob.Free;
btOn.Enabled:= true;
btOff.Enabled:= false;
pnMov.Enabled:= false;
end;
procedure
TForm1.btTurboClick(Sender: TObject);
begin
rob.HiTorq := not rob.HiTorq;
if rob.HiTorq
then btTurbo.Caption := 'Turbo on'
else btTurbo.Caption := 'Turbo off'
end;
procedure
TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
case Key of
'8': begin
rob.Ahead;
btAhead.SetFocus;
end;
'2': begin
rob.Back;
btBack.SetFocus;
end;
'5': begin
rob.Stop;
btStop.SetFocus;
end;
'7': begin
rob.Left;
btLeft.SetFocus;
end;
'9': begin
rob.Right;
btRight.SetFocus;
end;
'4': begin
rob.VeryLeft;
btVLeft.SetFocus;
end;
'6': begin
rob.VeryRight;
btVRight.SetFocus;
end;
'1': begin
rob.SpinLeft;
btSLeft.SetFocus;
end;
'3': begin
rob.SpinRight;
btSRight.SetFocus;
end;
end;
end;
procedure
TForm1.FormCreate(Sender: TObject);
begin
seTime.Enabled:= true;
end;
procedure
TForm1.btLeftClick(Sender: TObject);
begin
Rob.Left;
end;
procedure
TForm1.btVLeftClick(Sender: TObject);
begin
rob.VeryLeft;
end;
procedure
TForm1.btSLeftClick(Sender: TObject);
begin
rob.SpinLeft;
end;
procedure
TForm1.btAheadClick(Sender: TObject);
begin
rob.Ahead;
end;
procedure
TForm1.btStopClick(Sender: TObject);
begin
rob.Stop;
end;
procedure
TForm1.btBackClick(Sender: TObject);
begin
rob.Back;
end;
procedure
TForm1.btRightClick(Sender: TObject);
begin
rob.Right;
end;
procedure
TForm1.btVRightClick(Sender: TObject);
begin
rob.VeryRight;
end;
procedure
TForm1.btSRightClick(Sender: TObject);
begin
rob.SpinRight;
end;
procedure
TForm1.seTimeChange(Sender: TObject);
begin
rob.PulseTime:= seTime.Value;
end;
Índice
2) Visão geral do projeto3) Fonte de alimentação para testes de bancada
4) A porta paralela
5) Motores de passo
6) Montagem dos circuitos
7) Montagem do robô
8) Alimentação
9) Software de navegação
10) Conclusão
Apêndice - Duplicando polias e engrenagens
Nenhum comentário:
Postar um comentário