/// <summary> /// Evaluate the provided IBlackBox. /// </summary> public FitnessInfo Evaluate(IBlackBox box) { _evalCount++; // Initialise state. SinglePoleStateData state = new SinglePoleStateData(); state._poleAngle = SixDegrees; // Run the pole-balancing simulation. int timestep = 0; for (; timestep < _maxTimesteps; timestep++) { // Provide state info to the black box inputs (normalised to +-1.0). box.InputSignalArray[0] = state._cartPosX / _trackLengthHalf; // cart_pos_x range is +-trackLengthHalfed. Here we normalize it to [-1,1]. box.InputSignalArray[1] = state._cartVelocityX / 0.75; // cart_velocity_x is typically +-0.75 box.InputSignalArray[2] = state._poleAngle / TwelveDegrees; // pole_angle is +-twelve_degrees. Values outside of this range stop the simulation. box.InputSignalArray[3] = state._poleAngularVelocity; // pole_angular_velocity is typically +-1.0 radians. No scaling required. // Activate the network. box.Activate(); // Calculate state at next timestep given the black box's output action (push left or push right). SimulateTimestep(state, box.OutputSignalArray[0] > 0.5); // Check for failure state. Has the cart run off the ends of the track or has the pole // angle gone beyond the threshold. if ((state._cartPosX < -_trackLengthHalf) || (state._cartPosX > _trackLengthHalf) || (state._poleAngle > _poleAngleThreshold) || (state._poleAngle < -_poleAngleThreshold)) { break; } } if (timestep == _maxTimesteps) { _stopConditionSatisfied = true; } // The controller's fitness is defined as the number of timesteps that elapse before failure. double fitness = timestep; return(new FitnessInfo(fitness, fitness)); }
/// <summary> /// Calculates a state update for the next timestep using current model state and a single 'action' from the /// controller. The action specifies if the controller is pushing the cart left or right. Note that this is a binary /// action and therefore full force is always applied to the cart in some direction. This is the standard model for /// the single pole balancing task. /// </summary> /// <param name="state">Model state.</param> /// <param name="action">push direction, left(false) or right(true). Force magnitude is fixed.</param> private void SimulateTimestep(SinglePoleStateData state, bool action) { //float xacc,thetaacc,force,costheta,sintheta,temp; double force = action ? ForceMag : -ForceMag; double cosTheta = Math.Cos(state._poleAngle); double sinTheta = Math.Sin(state._poleAngle); double tmp = (force + (PoleMassLength * state._poleAngularVelocity * state._poleAngularVelocity * sinTheta)) / TotalMass; double thetaAcc = ((Gravity * sinTheta) - (cosTheta * tmp)) / (Length * (FourThirds - ((MassPole * cosTheta * cosTheta) / TotalMass))); double xAcc = tmp - ((PoleMassLength * thetaAcc * cosTheta) / TotalMass); // Update the four state variables, using Euler's method. state._cartPosX += TimeDelta * state._cartVelocityX; state._cartVelocityX += TimeDelta * xAcc; state._poleAngle += TimeDelta * state._poleAngularVelocity; state._poleAngularVelocity += TimeDelta * thetaAcc; state._action = action; }