Quote:
Originally Posted by JohnSmooth42
Here is my implementation of your library for use with encoders on a robot.
https://github.com/DannyMoses/Neural..._implementaion
It's definitely not very polished(I basically copied your example). Am I doing it right? I don't have a robot to test this on right now so I would value any feedback. I also just want to say that I think this is probably the future of FRC programming right here.
|
Extremely long post incoming, but I feel others would benefit from reading this as it is an actual implementation of it.
You are somewhat there.
Here is something that I found immediately:
//clear inputs and results to ensure no lurking data
inputVals.clear();
resultValues.clear();
//get your data, populate it in inputVals
//with inputVals[trainingPass].push_back(int)
// Get new input data and feed it forward:
net.feedForward(inputVals);
can you see what's wrong? Two things actually. One is that your input data vectors are empty (of size zero). The other thing is that you aren't sending what you want to train on (your inputs). By doing the latter you willing fix the first issue of the empty vector.
Another thing that is wrong: your architecture is 1-4-4-1, yet you have 2 target values. This is another dimensional error. This time instead of not having dimensional, you have too many!
Alright, moving on. So you want the robot to move 10 feet forward and 10 feet back, correct? What should be your input? What should your output be?
Let's redefine the problem for an easier first time implementation: let's move x feet forward, with the case of x being 10. You might want to decrease it to maybe 3 so you don't have to go chasing a robot around, just a thought.
So your input can be however many feet you want to move forward. In code that is
inputVals.push_back(10); //feet
what is your targetValue? Two approaches come to mind immediately. The first is your target is to travel 10 feet, so
targetVals.push_back(10); //feet.
There is a lot of room to expand on this. For instance, you could input your robot's position in x and y as two inputs, and your desired position be your target values x and y.
Moving on.
float outputVal = (float) motor_values.back();
myRobot.Drive(outputVal, 0.0f);
the .back member function of a vector calls the last value stored in the vector. What is the drive system of this robot? This assumes you only have one motor controlling your entire robot, or that you are only going to use one value of your motor hidden layer. Either way, it is sub optimal for two reasons. Your motor hidden layer has 4 nodes. If you want to go this route, you need to make you NN architecture account for it. That is, 1-n-1-1, where n is any number of nodes you desire.
Now you wait 50 (ms?). This may not be what you want, either. The neural network will try to find the motor value to travel x feet in 50 ms, and in your case that is 10 feet. You need to your robot time to try to get there based off the values you got from the network, maybe 5 seconds? Be careful though. When you first declare a NN, the weights are random, meaning it has a 50% chance of going backwards, so give the robot some room. I'd suggest doing this in an open area like a gym. A cool thing about this is that you don't have to recent the robot per iteration (unless you are getting close to a wall), because each iteration assume x feet from your current location.
I'm not familiar with FRC code anymore, but you may need to set motor values to 0 after the wait.
Now you get your updated sensor data:
results.push_back(leftEncoder.GetDistance() + rightEncoder.GetDistance() / 2);
What you did was right, however your units might not match up. I don't unit of length .GetDistance() returns. You may need to do a simple conversion.
You set this as your output layer, then you train through backpropagation, which is correct.
Then you wait until your network converges and exit.
Yikes. I just realized I forgot to save the state of the network.....that's rather important. I'll work on that asap.
Now your NN is trained properly to go x feet. But it isn't a general solution just yet. You need to test multiple inputs until each one gives the correct output, then send them all through again to ensure that your network is good.
Lastly, you'd want to save the state of this network (which I also haven't written code for yet) so you can load it and go from there.
When you load in a network to give you proper motor values, you do not need to apply backprop to it as it is already trained how you like it. So the code you need when you're done is:
Net net(NN.txt); //file that contains the state of the good NN
vector<double> targetValues(int x);
vector<double> inputValues(int y);
net.feedForward(inputVals);
vector<double> motor_values;
motor_values = net.getLayerValues(int z);
//rest of code that uses motor values
So great, you now have code that makes the robot go x feed forward in n time. As I mentioned earlier, you can get much more fancy with it. Let's bump it up to swerve drive. Your input could be your x and y position (could be assumed to be 0 0), the data you you get from the network could be heading and motor value, and your result would be your new robot position. Another cool addition would be to use the time you want the robot to complete it as an input. This way each distance is not treated equally in terms of time you give it to travel, which will allow for more robust autonomous routines. Yet another cool addition to the inputs would be battery voltage. This way if you have a bad battery in but trained on a good one, it won't matter because the network will generalize to work at every functioning voltage.
Hope this helped! Let me know if you need more assistance or if something doesn't make sense. There is a decent chance I messed up explaining something. It's been a long day.
Thanks for the last part, but the Poofs already have this beat by a lightyear with their 2014 autonomous.
Implementation note: you may want to only run through the network while training when you push a key, as well as add an emergency stop key stroke.
Quote:
Originally Posted by lopsided98
I have no previous experience with neural networks, so I'll probably have a lot of questions in the future, but for now I was wondering if there is some way to decide how to structure the hidden layers. In your example, you use 4 nodes, one for each wheel and two layers. What is the reason for this?
Thanks.
|
Adding this to the post as well because others could benefit from reading it.
Deciding the architecture of neural networks is a bit of a dark art. It is something that people just accept. As a side project in the lab, I am testing how different architectures of networks affects performance and rate of convergence (in terms of speed and iterations required). Hopefully I'll have some form of a paper by the end of summer to share with whomever is interested.
The reasoning behind by hidden layer architecture is that I am assuming a nonlinear relationship between the inputs and the proper motor values. If I don't have that second hidden layer then each motor values can be calculated by summing the inputs * weight_ij. With the addition of a hidden layer, it still allows for the there to be a linear relationship between the inputs and motor values, but it is also able to account for if the relationship is say, logarithmic.
I highly recommend to play around with different architectures.
I spoke with a buddy of mine at college and he said he'd be willing to help me include a gui so the user can visually see the network adapting, which would be really cool.