NeuralTargetingBot

From Robowiki
Revision as of 08:35, 11 August 2017 by Dsekercioglu (talk | contribs) (Made a NeuralTargetingBot using Roboneural)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Neural Networks

Neural Networks are machine learning systems loosely based on brains. Also they are not very popular in roborumble.

Making a Simple Neural Targeting Bot Using Roboneural

Utilities

Roboneural(There is a link in it's page).

    public static class GunUtils {

        public static double absoluteBearing(Point2D.Double l1, Point2D.Double l2) {
            return Math.atan2(l2.x - l1.x, l2.y - l1.y);
        }

        public static int limit(int min, int val, int max) {
            return Math.max(min, Math.min(val, max));
        }
    }

Just limiting a number between to numbers and taking the absolute bearing between 2 points.

Variables
    static final int BINS = 51;
    static final int MIDDLE_BIN = 25;
    static final double FIRE_POWER = 1.95;
    int[] splitNum = new int[]{9, 9};
    public static MultiLayerPerceptron mlp = new MultiLayerPerceptron(new int[]{18, BINS},
            new ActivationFunction[]{new Sigmoid()}, 0.1, 1);
    /*
        Creating a MultiLayerPerceptron is easy.
        First is how many cells for the nth layer.
        Second is the activation functions for hidden and output cells.
        Third is learning rate
        Fourth is batch size
     */

    Point2D.Double myLocation = new Point2D.Double();
    Point2D.Double enemyLocation = new Point2D.Double();

    ArrayList<Wave> gfWaves = new ArrayList<>();
    ArrayList<Wave> gfWavesToRemove = new ArrayList<>();

    public static ArrayList<double[]> neuralNetworkInput = new ArrayList<>();
    public static ArrayList<double[]> neuralNetworkOutput = new ArrayList<>();
    
    /*
    For saving past information to train our MLP.
    */

    public void run() { //Radar Stuff and Colors.
        setBodyColor(Color.ORANGE);
        setGunColor(Color.BLUE);
        setRadarColor(Color.CYAN);
        setScanColor(Color.CYAN);
        setAdjustGunForRobotTurn(true);
        setAdjustRadarForGunTurn(true);
        for (;;) {
            turnRadarRightRadians(Double.POSITIVE_INFINITY);
        }
    }


Wave Stuff
    public class Wave {

        double power;
        double velocity;
        double absoluteBearing;
        double distanceTraveled;
        int lateralDirection;
        double mea;
        double binWidth;
        Point2D.Double source;

        double[] input;

        public Wave(Point2D.Double source, double power, double absoluteBearing, int lateralDirection) {
            this.source = (Point2D.Double) source.clone();
            this.power = power;
            this.velocity = 20 - 3 * power;
            this.absoluteBearing = absoluteBearing;
            mea = 8 / velocity;
            binWidth = mea / BINS * 2;
            this.lateralDirection = lateralDirection;
        }

        public int update() {
            distanceTraveled += velocity;
            if (distanceTraveled > source.distance(enemyLocation)) {
                gfWavesToRemove.add(this);//These will be deleted.
                int bin = (int) Math.round(((Utils.normalRelativeAngle(GunUtils.absoluteBearing(source, enemyLocation) - absoluteBearing))
                        / (lateralDirection * binWidth)) + MIDDLE_BIN);
                return GunUtils.limit(0, bin, BINS - 1);//Hit guess factor
            }
            return -1;//If it didn't hit we will know that because of -1.
        }

        public double getFiringAngle(int firingBin) {
            return absoluteBearing + (lateralDirection * binWidth) * (firingBin - MIDDLE_BIN);//Producing a firing angle from a bin
        }
    }
Main code
    public void onScannedRobot(ScannedRobotEvent e) {
        myLocation.setLocation(getX(), getY());//Setting our location
        double absBearing = e.getBearingRadians() + getHeadingRadians();//For radar and targeting
        double distance = e.getDistance(); //For learning enemy location
        enemyLocation.setLocation(myLocation.x + Math.sin(absBearing) * distance,
                myLocation.y + Math.cos(absBearing) * distance);

        setTurnRadarRightRadians(Utils.normalRelativeAngle(absBearing - getRadarHeadingRadians()) * 2);//Locking the radar
        double enemyVelocity = e.getVelocity();
        double enemyLateralVelocity = (enemyVelocity * Math.sin(e.getHeadingRadians() - absBearing));//Some data
        double enemyAdvancingVelocity = (enemyVelocity * -Math.cos(e.getHeadingRadians() - absBearing));//Some data
        int enemyLateralDirection = enemyLateralVelocity >= 0 ? 1 : -1;

        double[] data = new double[]{enemyLateralVelocity, enemyAdvancingVelocity};//Normal Data
        double[] preprocessedData = dsekercioglu.roboneural.format.FeatureSplitter.split(data, splitNum);
        //Preprocessing the data to make it more useful for small networks.

        Wave w = new Wave(myLocation, FIRE_POWER, absBearing, enemyLateralDirection);//Creating a wave
        w.input = preprocessedData;
        gfWaves.add(w);

        int firingBin = dsekercioglu.roboneural.format.Utils.getBin(mlp.getOutput(preprocessedData));//Getting the bestBin
        double firingAngle = w.getFiringAngle(firingBin);
        setTurnGunRightRadians(Utils.normalRelativeAngle(firingAngle - getGunHeadingRadians()));
        setFire(FIRE_POWER);
        train(); //For training the network
        updateWaves(); //For updating the waves
    }
Updating the Waves and Training Our MultiLayerPerceptron
    public void updateWaves() {
        for (Wave w : gfWaves) {
            int result = w.update();
            if (result != -1) { //Here we use the -1 to understand if the wave hit or didn't.
                neuralNetworkInput.add(0, w.input); //We add to the beginning to make training easier.
                double[] bins = new double[BINS];
                bins[result] = 1;//We set the correct gf. Others are zero initially.
                neuralNetworkOutput.add(0, bins); //Adding it.
            }
        }
        gfWaves.removeAll(gfWavesToRemove);//Removing the hit waves.
        gfWavesToRemove.clear();//Clearing the list.
    }

    public void train() {
        if (!neuralNetworkInput.isEmpty()) {
            for (int i = 0; i < 25; i++) {
                int index = (int) (Math.random() * Math.min(200, neuralNetworkInput.size())); //Training the last 200 waves 25 times.
                mlp.backPropogate(neuralNetworkInput.get(index), neuralNetworkOutput.get(index));
            }
        }
    }

Notes

To make it more powerful there is a lot more to do:
    • Changing the training policy.
    • Increasing the number of data.
    • Combining with another network.
    • Different activation functions(For example SoftPlus(SmoothMax))
    • Changing the output type.
    • Making an anti-surfer gun.(It can be really strong)