예제 #1
0
        /// <summary>
        /// Create a 3X3 grid of paper colors using a source image and the 
        /// contours for each paper color.
        /// </summary>
        /// <param name="contourImage">Source image.</param>
        /// <param name="yellowContours">Yellow paper contours.</param>
        /// <param name="greenContours">Green paper contours.</param>
        /// <returns></returns>
        public Paper[,] detectColumnPaperColors(Image<Bgr, byte> contourImage, List<Contour<Point>> yellowContours, List<Contour<Point>> greenContours)
        {
            Paper[,] papers = new Paper[3,3];

            for (int i = 0; i < papers.GetLength(0); i++)
            {
                for(int j = 0; j < papers.GetLength(1); j++)
                {
                    papers[i, j] = new Paper();
                    papers[i, j].Color = PaperColor.UNKNOWN;
                }
            }

            detectColumns(contourImage, papers, yellowContours, PaperColor.YELLOW);
            detectColumns(contourImage, papers, greenContours, PaperColor.GREEN);

            return papers;
        }
예제 #2
0
        /// <summary>
        /// Adds paper colors from a contour into the grid.
        /// </summary>
        /// <param name="contourImage">Source image.</param>
        /// <param name="papers">Grid to add paper colors to.</param>
        /// <param name="paperContours">Contours to add to grid.</param>
        /// <param name="paperColor">Color of paper to add to grid.</param>
        private void detectColumns(Image<Bgr, byte> contourImage, Paper[,] papers, List<Contour<Point>> paperContours, PaperColor paperColor)
        {
            foreach (Contour<Point> paperContour in paperContours)
            {
                int x;
                int xMidPoint = paperContour.BoundingRectangle.X + paperContour.BoundingRectangle.Width / 2;

                if (xMidPoint < contourImage.Width * LEFT_WIDTH_THRESHOLD)
                {
                    x = 0;
                }
                else if (xMidPoint > contourImage.Width * RIGHT_WIDTH_THRESHOLD)
                {
                    x = 2;
                }
                else
                {
                    x = 1;
                }

                int y;
                int yMidPoint = paperContour.BoundingRectangle.Y + paperContour.BoundingRectangle.Height / 2;

                if (yMidPoint < contourImage.Height * TOP_HEIGHT_THRESHOLD)
                {
                    y = 0;
                } else if(yMidPoint > contourImage.Height * BOTTOM_HEIGHT_THRESHOLD)
                {
                    y = 2;
                }
                else
                {
                    y = 1;
                }

                Paper currentPaper = papers[y, x];
                currentPaper.Color = paperColor;
                currentPaper.XMidPoint = xMidPoint;
                currentPaper.YMidPoint = yMidPoint;
                currentPaper.ParentImageWidth = contourImage.Width;
                currentPaper.ParentImageHeight = contourImage.Height;
            }
        }
예제 #3
0
 /// <summary>
 /// Do nothing.
 /// See <see cref="State.WAIT_FOR_RUN"/>.
 /// </summary>
 /// <param name="papers">Current Paper Grid</param>
 /// <returns>Next State</returns>
 private State waitForRun(Paper[,] papers)
 {
     Stop();
     this.paperColumns = new List<PaperColor[]>();
     return State.WAIT_FOR_RUN;
 }
예제 #4
0
        /// <summary>
        /// Steer to move to the next column.
        /// See <see cref="State.STEER_TO_MOVE"/>.
        /// </summary>
        /// <remarks>This always applies a static 
        /// steering correction when moving left because the robot would 
        /// not turn adequately under testing. This should be revisited 
        /// for specific applications.</remarks>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.STEER_ANGLE_CORRECTION"/></returns>
        private State steerToMove(Paper[,] papers)
        {
            if (this.direction == Direction.LEFT)
            {
                this.motor.turn90DegreesLeft();
                this.motor.turn3DegreesLeft();
            }
            else
            {
                this.motor.turn90DegreesRight();
            }

            return State.STEER_ANGLE_CORRECTION;
        }
예제 #5
0
        /// <summary>
        /// Steer to view the papers in a column.
        /// See <see cref="State.STEER_TO_DETECT"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.COLUMN_ANGLE_CORRECTION"/></returns>
        private State steerToDetect(Paper[,] papers)
        {
            if(this.direction == Direction.LEFT)
            {
                this.motor.turn90DegreesRight();
            }
            else
            {
                this.motor.turn90DegreesLeft();
            }

            return State.COLUMN_ANGLE_CORRECTION;
        }
예제 #6
0
        /// <summary>
        /// Perform a corrective turn, so the robot drives perpendicular 
        /// to the paper.
        /// See <see cref="State.STEER_ANGLE_CORRECTION"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.STEER_ANGLE_CORRECTION"/> if a 
        /// correction is made. <see cref="State.MOVE_FORWARD"/> if 
        /// no correction was made.</returns>
        private State steerAngleCorrection(Paper[,] papers)
        {
            if (this.direction == Direction.LEFT)
            {
                if (papers[2, 2].Color != PaperColor.UNKNOWN)
                {
                    this.motor.turn3DegreesLeft();
                    return State.STEER_ANGLE_CORRECTION;
                }
            }
            else
            {
                if (papers[2, 0].Color != PaperColor.UNKNOWN)
                {
                    this.motor.turn3DegreesRight();
                    return State.STEER_ANGLE_CORRECTION;
                }
            }

            return State.MOVE_FORWARD;
        }
예제 #7
0
        /// <summary>
        /// Steer 720 Degrees to indicate that the digit has been 
        /// detected.
        /// See <see cref="State.WAIT_FOR_RUN"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns></returns>
        private State steer720Degrees(Paper[,] papers)
        {
            // 8 90 degree turns is 720 degrees
            for(int i = 0; i < 8; i++)
            {
                this.motor.turn90DegreesRight();
            }

            return State.WAIT_FOR_RUN;
        }
예제 #8
0
        /// <summary>
        /// Drives forward slightly if the robot did not travel 
        /// an adequate distance.
        /// See <see cref="State.MOVE_FORWARD_CORRECTION"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.MOVE_FORWARD_CORRECTION"/> 
        /// if a correction is made, otherwise 
        /// <see cref="State.STEER_TO_DETECT"/></returns>
        private State moveForwardCorrection(Paper[,] papers)
        {
            /*
            Paper paper;

            if (this.direction == Direction.LEFT)
            {
                paper = papers[1, 2];
            }
            else
            {
                paper = papers[1, 0];
            }

            if (paper.Color != PaperColor.UNKNOWN)
            {
                if (paper.YMidPoint < paper.ParentImageHeight * FORWARD_CORRECTION_VERTICAL_THRESHOLD)
                {
                    this.motor.driveForwardCorrection();

                    return State.MOVE_FORWARD_CORRECTION;
                }
            }
             */

            return State.STEER_TO_DETECT;
        }
예제 #9
0
        /// <summary>
        /// Drives forward to the next column.
        /// See <see cref="State.MOVE_FORWARD"/>.
        /// </summary>
        /// <remarks>A static correction is always added when the robot is 
        /// moving right to compensate for uneven motor speeds.</remarks>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.MOVE_FORWARD_CORRECTION"/></returns>
        private State moveForward(Paper[,] papers)
        {
            this.motor.driveForward();

            if (this.direction == Direction.RIGHT)
            {
                this.motor.driveForwardCorrection();
                this.motor.driveForwardCorrection();
                this.motor.driveForwardCorrection();
            }

            return State.MOVE_FORWARD_CORRECTION;
        }
예제 #10
0
        /// <summary>
        /// Detects and stores the center columns in the current paper grid.
        /// See <see cref="State.COLUMN_DETECTION"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.STEER_TO_MOVE"/> if there are more 
        /// columns. <see cref="State.CALCULATE_DIGIT"/> if all columns 
        /// have now been checked.</returns>
        private State columnDetection(Paper[,] papers)
        {
            PaperColor[] paperColorColumn = new PaperColor[3];

            // Top Column
            paperColorColumn[0] = papers[0, 1].Color;

            // Middle Column
            paperColorColumn[1] = papers[1, 1].Color;

            // Bottom Column
            paperColorColumn[2] = papers[2, 1].Color;

            this.paperColumns.Add(paperColorColumn);

            if (this.paperColumns.Count < COLUMN_COUNT)
            {
                return State.STEER_TO_MOVE;
            }
            else
            {
                return State.CALCULATE_DIGIT;
            }
        }
예제 #11
0
        /// <summary>
        /// Checks of the column is centered in the robot's field of 
        /// vision and makes corrective turns if it isn't.
        /// See <see cref="State.COLUMN_ANGLE_CORRECTION"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.COLUMN_DETECTION"/> if the column 
        /// is centered, otherwise <see cref="State.COLUMN_DETECTION"/>.</returns>
        private State columnAngleCorrection(Paper[,] papers)
        {
            Paper topCenterCellPaper = papers[0,1];

            if(topCenterCellPaper.XMidPoint < topCenterCellPaper.ParentImageWidth * COLUMN_ANGLE_LEFT_CORRECTION_THRESHOLD)
            {
                this.motor.turn3DegreesLeft();
            }
            else if (topCenterCellPaper.XMidPoint > topCenterCellPaper.ParentImageWidth * COLUMN_ANGLE_RIGHT_CORRECTION_THRESHOLD)
            {
                this.motor.turn3DegreesRight();
            } else
            {
                return State.COLUMN_DETECTION;
            }

            return State.COLUMN_ANGLE_CORRECTION;
        }
예제 #12
0
        /// <summary>
        /// Check what side of the robot the columns are on and 
        /// sets the direction used throughout the state machine.
        /// See <see cref="State.CHECK_DIRECTION"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.COLUMN_ANGLE_CORRECTION"/></returns>
        private State checkDirection(Paper[,] papers)
        {
            int leftPaperCount = 0;
            int rightPaperCount = 0;

            for(int i = 0; i < papers.GetLength(0); i++)
            {
                for(int j = 0; j < papers.GetLength(1); j++)
                {
                    if(papers[i,j].Color != PaperColor.UNKNOWN)
                    {
                        if(j == 0)
                        {
                            leftPaperCount++;
                        } else if(j == 2)
                        {
                            rightPaperCount++;
                        }
                    }
                }

                if (leftPaperCount > rightPaperCount)
                {
                    this.direction = Direction.LEFT;
                }
                else
                {
                    this.direction = Direction.RIGHT;
                }
            }

            return State.COLUMN_ANGLE_CORRECTION;
        }
예제 #13
0
        /// <summary>
        /// Calculates the digit represented by all of the 
        /// paper color columns. The detected digit is sent 
        /// to the UI using 
        /// <see cref="IDigitDetectionCallback.DigitDetected(int, PaperColor[])"/>.
        /// </summary>
        /// <param name="papers">Current Paper Grid</param>
        /// <returns><see cref="State.STEER_720_DEGREES"/></returns>
        private State calculateDigit(Paper[,] papers)
        {
            // The columns are read top to bottom; however, the digit needs to
            // be  bottom to top, which translate into left to right when the
            // digit is compared.
            for (int i = 0; i < this.paperColumns.Count; i++)
            {
                PaperColor temp = this.paperColumns[i][0];
                this.paperColumns[i][0] = this.paperColumns[i][2];
                this.paperColumns[i][2] = temp;
            }

            if (this.direction == Direction.LEFT)
            {
                this.paperColumns.Reverse();
            }

            // Convert paper color columns to an array
            PaperColor[] paperColors = new PaperColor[this.paperColumns.Count * 3];

            for (int i = 0; i < paperColumns.Count; i++)
            {
                for(int j = 0; j < 3; j++)
                {
                    paperColors[i * papers.GetLength(0) + j] = this.paperColumns[i][j];
                }
            }

            int matchNumber = 0;
            int matchCount = 0;

            // Find the digit that is the closest match. The idea behind
            // this algorithm is that the closest match is the digit with
            // the most papers in common with the digit represented by
            // the paper viewed by the robot. In the event that the robot
            // is unable to read a sheet of paper, this algorithm assumes
            // that it would have been a match.
            for(int i = 0; i < this.digitsToMatch.GetLength(0); i++)
            {
                int currentMatchCount = 0;

                for(int j = 0; j < this.digitsToMatch.GetLength(1); j++)
                {
                    if(paperColors[j] == PaperColor.UNKNOWN)
                    {
                        currentMatchCount++;
                    } else if(paperColors[j] == this.digitsToMatch[i, j])
                    {
                        currentMatchCount++;
                    }
                }

                if(currentMatchCount > matchCount)
                {
                    matchNumber = i;
                    matchCount = currentMatchCount;
                }
            }

            this.callback.DigitDetected(matchNumber, paperColors);

            return State.STEER_720_DEGREES;
        }
예제 #14
0
        /// <summary>
        /// Run the state machine for the current grid of papers detected.
        /// </summary>
        /// <param name="papers">Current grid of papers detected.</param>
        public void processDigitDetection(Paper[,] papers)
        {
            if(this.stop)
            {
                this.state = State.WAIT_FOR_RUN;
            }

            if (this.initialFrameCount < INITIAL_FRAMES_IGNORED)
            {
                this.initialFrameCount++;
            }
            else
            {
                this.initialFrameCount = 0;

                switch (this.state)
                {
                    case State.WAIT_FOR_RUN:
                        this.state = this.waitForRun(papers);
                        break;
                    case State.CHECK_DIRECTION:
                        this.state = this.checkDirection(papers);
                        break;
                    case State.COLUMN_ANGLE_CORRECTION:
                        this.state = this.columnAngleCorrection(papers);
                        break;
                    case State.COLUMN_DETECTION:
                        this.state = this.columnDetection(papers);
                        break;
                    case State.STEER_TO_MOVE:
                        this.state = this.steerToMove(papers);
                        break;
                    case State.STEER_ANGLE_CORRECTION:
                        this.state = this.steerAngleCorrection(papers);
                        break;
                    case State.MOVE_FORWARD:
                        this.state = this.moveForward(papers);
                        break;
                    case State.MOVE_FORWARD_CORRECTION:
                        this.state = this.moveForwardCorrection(papers);
                        break;
                    case State.STEER_TO_DETECT:
                        this.state = this.steerToDetect(papers);
                        break;
                    case State.CALCULATE_DIGIT:
                        this.state = this.calculateDigit(papers);
                        break;
                    case State.STEER_720_DEGREES:
                        this.state = this.steer720Degrees(papers);
                        break;
                    default:
                        this.state = this.waitForRun(papers);
                        break;
                }
            }
        }
예제 #15
0
        /// <summary>
        /// Display the current grid of paper colors on the UI.
        /// </summary>
        /// <param name="papers">Current paper grid.</param>
        private void displayPapers(Paper[,] papers)
        {
            if (this.InvokeRequired)
            {
                DisplayPaperCallback callback = new DisplayPaperCallback(displayPapers);

                try
                {
                    this.Invoke(callback, new object[] { papers });
                }
                catch (ObjectDisposedException)
                {
                    // No Op
                }
            }
            else
            {
                this.y0x0ColTxt.Text = PaperColorUtils.PaperColorToString(papers[0, 0].Color);
                this.y0x1ColTxt.Text = PaperColorUtils.PaperColorToString(papers[0, 1].Color);
                this.y0x2ColTxt.Text = PaperColorUtils.PaperColorToString(papers[0, 2].Color);
                this.y1x0ColTxt.Text = PaperColorUtils.PaperColorToString(papers[1, 0].Color);
                this.y1x1ColTxt.Text = PaperColorUtils.PaperColorToString(papers[1, 1].Color);
                this.y1x2ColTxt.Text = PaperColorUtils.PaperColorToString(papers[1, 2].Color);
                this.y2x0ColTxt.Text = PaperColorUtils.PaperColorToString(papers[2, 0].Color);
                this.y2x1ColTxt.Text = PaperColorUtils.PaperColorToString(papers[2, 1].Color);
                this.y2x2ColTxt.Text = PaperColorUtils.PaperColorToString(papers[2, 2].Color);
            }
        }