sexta-feira, 17 de fevereiro de 2012

9) Software de navegação


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;

end.



Índice

2) Visão geral do projeto
3) 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