Moderators: roger, 99jonathan, imaqine
import lejos.nxt.*;
public class Sejway2 {
// PID constants
final int KP = 28;
final int KI = 4;
final int KD = 33;
final int SCALE = 18;
// Global vars:
int offset;
int prev_error;
float int_error;
TiltSensor ts;
public Sejway2() {
ts = new TiltSensor(SensorPort.S4);
Motor.B.regulateSpeed(false);
Motor.C.regulateSpeed(false);
}
public void getBalancePos() {
// Wait for user to balance and press orange button
while (!Button.ENTER.isPressed()) {
// NXT must be balanced.
offset = ts.getZTilt();
LCD.clear();
LCD.drawInt(offset, 2, 4);
LCD.refresh();
}
}
public void pidControl() {
while (!Button.ESCAPE.isPressed()) {
int normVal = ts.getZTilt();
// Proportional Error:
int error = normVal - offset;
// Adjust Y readings:
if (error < 0) error = (int)(error * 1.8F);
// Integral Error:
int_error = ((int_error + error) * 2)/3;
// Derivative Error:
int deriv_error = error - prev_error;
prev_error = error;
int pid_val = (int)(KP * error + KI * int_error + KD * deriv_error) / SCALE;
if (pid_val > 100)
pid_val = 100;
if (pid_val < -100)
pid_val = -100;
// Power derived from PID value:
int power = Math.abs(pid_val);
power = 55 + (power * 45) / 100; // NORMALIZE POWER
Motor.B.setPower(power);
Motor.C.setPower(power);
if (pid_val > 0) {
Motor.B.forward();
Motor.C.forward();
} else {
Motor.B.backward();
Motor.C.backward();
}
}
}
public void shutDown() {
// Shut down motors
Motor.B.flt();
Motor.C.flt();
}
public static void main(String[] args) {
Sejway2 sej = new Sejway2();
sej.getBalancePos();
sej.pidControl();
sej.shutDown();
}
}
package com.systronix.jcx;
import com.systronix.util.*;
import java.io.IOException;
/* Graphical simuation code
import javax.swing.*;
import java.awt.*;
*/
/**
* PID code developed for driving motors and following an object.
*
* @author Brandon Mansfield
* @version 0.4
* @see PID#VERSION
* <UL>
* <LI> 0.4 2006 april 18 Welding Torches - Added public attribute atCruiseSpeed. This value will keep track
* if PID loop is running at the default output. This fixed wrong behavior where the robot would move
* away from objects.
* <LI> 0.3 2006 april 08 Dustin Kopta, Chris Hansen - moved motor control to main method, main is only used
* for testing, look at main for an example of how to use this class
* <LI> 0.2 revision by Chris Hansen - fixed issue where goal was reached, but motors were still being driven
* which resulted in oscillation within a certain distance of goal. Also, created MIN_OUTPUT (which may need
* to be tuned) so that we only drive the motors with enough power necessary to move, and cleaned up code.
* <LI> 0.1 Code created to handle motor control with sonar data. Tuning still required and may
* be required for every bot as they often react differently.
* </UL>
* <P>PID loops are described <A HREF="http://en.wikipedia.org/wiki/PID">here</A> for further reference.</P>
* <P>This code is designed to be mostly self contained, assign a goal distance and give it the appropriate
* left and right ring buffers and it will initialize the motors, run in it's own thread (once started) and
* try and seek and find objects.</P>
*
* <P>Public methods allow the retrival of current motor values, getLeftOutput() and getRightOutput()</P>
*
* </UL>
*/
public class PID extends Thread
{
/**
* These constants will most likely need to be tuned on a per bot basis.
*/
private static final float fractP = 0.5f;
private static final float fractI = 0.1f;
// Brandon's values
//private static final float fractP = 0.4f;
//private static final float fractI = 0.08f;
/**
* Period (in milliseconds) which to sleep in PID.run()
*/
public static final int PID_PERIOD = 66;
/**
* Default "cruising" output level on a scale of 0 - 100.
* This is output when no object is detected.
*/
public static final int DEFAULT_OUTPUT = 50;
/**
* Any sonar reading (raw value) beyond this one will be ignored.
* This was determined via testing.
*/
private static final int maxRawValue = 500;
/**
* True iff thread is quitting. Changed with an external call to this.quit()
*/
private boolean quitting;
/**
* Basically the "return value" of the PID loop.
*/
private int output;
/**
* Goal distance, default is 250mm. Stored internally as raw units.
* Only set with setGoal() so that conversion is done.
*/
private int goal;
private int data;
private RingBuffer rbuffer = null;
public PID(RingBuffer rbuf)
{
init();
rbuffer = rbuf;
}
public void init()
{
output = 0;
setGoal(250);
quitting = false;
}
/**
* Sets the goal for PID loops in mm.
*
* @param newGoal new goal distance in mm.
*/
public void setGoal(int newGoalMm)
{
goal = (newGoalMm * 200) / 318;
}
public void quit() {
quitting = true;
}
// private static int convertToMm(int sonarVal)
// {
// return (sonarVal * 159) / 100;
// }
// get latest sample from the left RingBuffer
//static int leftError()
//{
//return 0;
//}
// get the 4th sample from the right RingBuffer
// private int lastError()
// {
// data = rbuffer.get(4);
// if (data > 0)
// return data-goal;
// else
// return 0;
// }
private int calcP(int error)
{
if (error == 0)
return 0;
else
return (int)(error*fractP);
}
private int calcI(int error)
{
if (error == 0)
return 0;
else
return (int)(error*fractI);
}
private void runPID()
{
int currentSample = rbuffer.get(0);
// ignore values greater than maxRawValue
if (currentSample >= maxRawValue) {
output = DEFAULT_OUTPUT;
return;
}
else // currentSample < maxSonarValue, do not ignore
currentSample -= goal;
//System.out.println("Current Error: " + currentSample);
int sum = currentSample+rbuffer.get(1)-goal+rbuffer.get(2)-goal+rbuffer.get(3)-goal+rbuffer.get(4)-goal;
output = calcP(currentSample) + calcI(sum);
if (output > 100)
output = 100;
else if (output < -100)
output = -100;
//System.out.println("output: " + output + " , P = " + calcP(currentSample) + ", I = " + calcI(sum));
}
public void run()
{
if (rbuffer == null)
return;
while (!quitting) // loop until quit() is called
{
try
{
runPID();
Util.waitWhile(PID_PERIOD);
}
catch (Exception e)
{
System.out.println("Exception in PID.run(): \n" + e.getMessage());
e.printStackTrace();
}
}
}
/**
* Gets the output which is updated periodically by PID.run()
* This output can be used as power levels to drive a motor (negative values indicate reverse).
*
* @return int from -100 to 100 (inclusive) based on error
*/
public int getOutput()
{
return output;
}
public void setBuffer(RingBuffer newBuffer)
{
rbuffer = newBuffer;
}
/**
* Used for testing only
* @param argv
*/
public static void main(String argv[])
{
try {
System.out.println("PID Main");
boolean ok = true;
Sonar sonarModule0 = Sonar.getSonar0();
Sonar sonarModule1 = Sonar.getSonar1();
RingBuffer right = Sonar.getRightBuffer();
RingBuffer left = Sonar.getLeftBuffer();
if (null==sonarModule0 || left==null) {
System.out.println("Sonar0 setup ERROR!");
ok = false;
}
if (null==sonarModule1 || right==null) {
System.out.println("Sonar1 setup ERROR!");
ok = false;
}
// if couldn't set up, can't use them, so bail out
if (!ok) return;
System.out.println("Starting Sonar thread");
Sonar.startSonar();
System.out.println("Initializing motors.");
Motor leftMotor = null;
Motor rightMotor = null;
try {
leftMotor = new Motor(Motor.MOTOR_PORT_1,0,Motor.SPI_SLAVE_SELECT_0);
rightMotor = new Motor(Motor.MOTOR_PORT_0,0,Motor.SPI_SLAVE_SELECT_0);
leftMotor.setState(Motor.MOTOR_BRAKE);
leftMotor.setState(Motor.MOTOR_BRAKE);
leftMotor.setMaximumPowerLevel(100);
rightMotor.setMaximumPowerLevel(100);
}
catch(IOException e)
{
System.out.println("**Unable to create motors****************************");
System.out.println(e);
System.out.println("*****************************************************");
return; // cannot continue
}
System.out.println("Creating PID threads.");
PID leftPid = new PID(left);
PID rightPid = new PID(right);
System.out.println("pid threads created");
System.out.println("calling pid.run()");
leftPid.start();
rightPid.start();
System.out.println("done calling run");
int leftOutput;
int rightOutput;
// print out distance and speed every second
for (;;) {
// Do not print these out more often than every 500ms
System.out.println("LeftPID: " + leftPid.getOutput());
System.out.println("RightPID: " + rightPid.getOutput());
System.out.println("");
// This is a Thread.sleep util from the SystronixUtils library
//Util.waitWhile(500);
leftOutput = leftPid.getOutput();
rightOutput = rightPid.getOutput();
if (leftOutput < 0) {
leftMotor.setState(Motor.MOTOR_REVERSE);
leftMotor.setPowerLevel(-1 * leftOutput);
}
else {
leftMotor.setState(Motor.MOTOR_FORWARD);
leftMotor.setPowerLevel(leftOutput);
}
if (rightOutput < 0) {
rightMotor.setState(Motor.MOTOR_REVERSE);
rightMotor.setPowerLevel(-1 * rightOutput);
}
else {
rightMotor.setState(Motor.MOTOR_FORWARD);
rightMotor.setPowerLevel(rightOutput);
}
Util.waitWhile(500);
}
/* Simulation code
JFrame sonarControlGui = new JFrame();
JPanel box = new JPanel();
box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS));
JSlider leftJS = new JSlider(JSlider.VERTICAL, 20, 30, 25);
leftJS.setPreferredSize(new Dimension(50, 200));
box.add(leftJS);
JSlider rightJS = new JSlider(JSlider.VERTICAL, 20, 30, 25);
rightJS.setPreferredSize(new Dimension(50, 200));
box.add(rightJS);
sonarControlGui.add(box);
sonarControlGui.setSize(new Dimension(30, 200));
sonarControlGui.setVisible(true);
while (true)
{
// Graphical simuation code
//int rightSample = rightJS.getValue();
//int leftSample = leftJS.getValue();
System.out.println("output: " + pid.getOutput());
try { Util.waitWhile(500); } catch (Exception e) {}
}
*/
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Users browsing this forum: No registered users and 2 guests