산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

인공 지능을 사용하는 미로 해결 로봇

구성품 및 소모품

Arduino Nano R3
× 1
SparkFun RedBot 센서 - 라인 팔로워
× 1
ZX03(TCRT5000 기반) 반사 적외선 센서(아날로그 출력)
× 2
Android 기기
× 1
RobotGeek 연속 회전 서보
× 2
4xAA 배터리 홀더
× 2

앱 및 온라인 서비스

Arduino IDE
MIT 앱 인벤터 2

이 프로젝트 정보

소개

이 튜토리얼은 제 마지막 프로젝트인 Line Follower Robot - PID Control - Android Setup에서 개발되었습니다. 라인 따라가기 기능이 있는 로봇을 갖게 되면 다음 자연스러운 단계는 로봇에게 어느 정도의 지능을 부여하는 것입니다. 그래서, 우리의 사랑하는 "로봇 렉스"는 "미궁"에서 가장 짧고 빠른 방법으로 탈출하는 방법을 지금 찾으려고 노력할 것입니다(그런데 그는 미노타우로스를 싫어합니다.

먼저 Maze의 차이점은 무엇인가요? 그리고 미궁 ? http://www.labyrinthos.net에 따르면, 영어권 세계에서는 미로의 자격을 갖추려면 디자인이 경로에서 선택권을 가져야 한다고 흔히 생각합니다. 분명히 여기에는 2D 미로를 포함하여 엔터테인먼트 공원 및 관광 명소에 현대적인 설치가 많이 포함될 것입니다. 대중의 합의에 따르면 미궁에는 입구에서 목표까지 가차 없이 이어지는 하나의 경로가 있지만, 종종 가장 복잡하고 구불구불한 경로가 있습니다.

대부분의 미로는 디자인이 아무리 복잡해 보일지라도 본질적으로 많은 접합부와 가지가 있는 하나의 연속적인 벽으로 형성되었습니다. 미로의 목표를 둘러싸고 있는 벽이 입구에서 미로의 둘레와 연결되어 있다면, 아무리 많은 우회로를 가더라도 한 손으로 벽에 접촉을 유지함으로써 미로를 해결할 수 있습니다. 이러한 '단순한' 미로는 "단순 연결 " 또는 "완벽한 미로 " 또는 다른 말로 루프가 없음을 포함합니다. .

프로젝트로 돌아가면 두 부분(또는 "pass "):

<울>
  • (첫 번째 패스) :로봇이 "알려지지 않은 완벽한 미로에서 탈출구를 찾습니다. ". 미로 속 어디에 두어도 상관없이 "해결책을 찾을 것입니다. ".
  • <울>
  • (두 번째 패스) :로봇이 가능한 미로 솔루션을 찾으면 "처음부터 끝까지 가장 짧은 경로를 찾는 솔루션을 최적화해야 합니다. ".
  • 아래 비디오는 Rex가 탈출구를 찾는 예를 보여줍니다. 로봇이 미로를 처음 탐색할 때는 당연히 많은 시간을 "생각 낭비하게 될 것입니다. " 교차로에서 무엇을 해야 하는지에 대한 정보입니다. 수많은 가능성을 테스트하면 여러 가지 잘못된 경로와 막다른 골목이 필요하므로 더 긴 경로를 실행하고 불필요한 "U-Turns를 수행해야 합니다. ". 이 "1차 통과" 동안 , 로봇은 경험을 축적하고 "메모 " 다양한 교차로 및 잘못된 분기 제거에 대해 설명합니다. "2nd Pass ", 로봇은 실수나 의심 없이 끝까지 직선적이고 빠르게 끝납니다. 이 튜토리얼을 따라 수행 방법을 자세히 살펴보겠습니다.

    1단계:자재 명세서

    재료 목록은 기본적으로 LEFT 및 RIGHT 교차점 감지에 대한 더 나은 정확도를 위해 2개의 추가 센서를 포함한다는 점을 제외하고는 Line Follower Robot에 사용된 것과 동일합니다.

    최종 로봇은 여전히 ​​매우 저렴합니다(약 $85.00):

    본체(필요 또는 사용 가능한 재료에 맞게 조정할 수 있음):

    <울>
  • 2 X 나무 정사각형(80X80mm)
  • <울>
  • 3 X 바인더 클립
  • <울>
  • 2 X 나무 바퀴(직경:50mm)
  • <울>
  • 1 X 볼 캐스터
  • <울>
  • 9 X 탄성 밴드
  • <울>
  • 3M 커맨드 프레임 스트립
  • <울>
  • 센서 고정용 플라스틱 조인트
  • <울>
  • 브레드보드 및 배선
  • <울>
  • 4XNi-Metal Hydride 배터리 2개 세트(각 세트마다 5V)
  • <울>
  • 2 X SM-S4303R 연속 회전 360도 플라스틱 서보
  • <울>
  • 아두이노 나노
  • <울>
  • HC-06 블루투스 모듈
  • <울>
  • 5 X 라인 센서(TCRT5000 4CH 적외선 라인 트랙 팔로워 센서 모듈 + 1개의 독립 트랙 센서)
  • <울>
  • 2 X ZX03(TCRT5000 기반) 반사형 적외선 센서(아날로그 출력)
  • <울>
  • LED 1개
  • <울>
  • 버튼 1개
  • 참고 :나는 위의 7번 항목을 아날로그 출력으로 사용했는데, 6번 항목과 같은 디지털 출력 센서가 없었기 때문입니다. 가능하면 모든 센서를 동일하게 하는 것이 이상적입니다. 또한 원래 5개의 센서만 유지하면서 프로젝트를 테스트했습니다. 작동하지만 교차로를 찾을 때 더 민감한 조정이 필요합니다.

    2단계:신체의 변화

    5개의 라인 추적 센서의 원래 세트를 제거하고 새로운 "Far LEFT" 수정 및 "맨 오른쪽 " 지지대 플라스틱 막대의 각 극단에 반사 센서가 있습니다. 가능한 한 7개의 센서를 정렬하는 것이 좋습니다.

    3단계:새 센서 설치 및 테스트

    이제 7개의 센서로 구성된 새로운 어레이 , 5개의 원본은 PID 제어(및 나중에 설명하는 "전체 라인" 감지) 전용으로 사용되고 2개의 새로운 것은 LEFT 및 RIGHT 교차 감지 전용으로 남은 방식으로 장착됩니다.

    간단한 검토로 5개의 원래 "디지털" 센서가 어떻게 작동하는지 기억해 보겠습니다.

    하나의 센서가 검은색 선과 관련하여 중앙에 있으면 해당 특정 센서만 HIGH를 생성합니다. 한편, 센서 2개가 동시에 검은색 선의 전체 너비를 덮을 수 있고 두 센서 모두에서 HIGH 신호를 생성할 수 있도록 센서 사이의 공간을 계산해야 합니다.

    2개의 새로운 "아날로그" 센서 작동 방식:

    센서 중 하나가 검은색 선과 관련하여 중앙에 있으면 출력은 일반적으로 "100" 미만의 Arduino ADC에서 출력을 생성하는 아날로그 값이 됩니다(ADC는 0에서 1023 사이의 출력을 생성함을 기억하십시오). 표면이 더 밝을수록 출력 값이 더 높아집니다(예를 들어 백서에 500~600을 테스트했습니다). 이 값은 귀하의 경우에 사용할 올바른 THRESHOLD 상수를 정의하기 위해 조명 및 표면 재료의 다양한 상황에서 테스트되어야 합니다(여기 그림 참조).

    Arduino 코드를 보면 각 센서는 특정 이름으로 정의됩니다(왼쪽에 있는 원래 Line Follow Sensor에는 "0 레이블이 지정되어야 함을 고려하십시오. "):

    <사전><코드> const int lineFollowSensor0 =12; //디지털 입력 사용 const int lineFollowSensor1 =18; //아날로그 핀 A4를 디지털 입력으로 사용 const int lineFollowSensor2 =17; //아날로그 핀 A3을 디지털 입력으로 사용 const int lineFollowSensor3 =16; //아날로그 핀 A2를 디지털 입력으로 사용 const int lineFollowSensor4 =19; //아날로그 핀 A5를 디지털 입력으로 사용 const int farRightSensorPin =0; //아날로그 핀 A0const int farLeftSensorPin =1; //아날로그 핀 A1

    기억하기 위해 라인을 따를 때 가능한 5개의 원래 센서 어레이 출력은 다음과 같습니다.

    <사전><코드>1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0

    2개의 새로운 항목이 추가되면 가능한 출력은 다음과 같습니다.

    <울>
  • 맨 왼쪽 센서:임계값보다 크거나 작은 아날로그 출력
  • <울>
  • Far RIGHT 센서:THRESHOLD보다 크거나 작은 아날로그 출력
  • 각 센서의 값을 저장하기 위해 원래 5개의 디지털 센서에 대한 어레이 변수가 생성됩니다.

    int LFSensor[5]={0, 0, 0, 0, 0}; 

    그리고 2개의 새로운 아날로그 센서에 대한 2개의 정수 변수:

    int farRightSensor =0;int farLeftSensor =0; 

    어레이 및 변수의 각 위치는 각 센서의 출력으로 지속적으로 업데이트됩니다.

    LFSensor[0] =digitalRead(lineFollowSensor0), LFSensor[1] =digitalRead(lineFollowSensor1), LFSensor[2] =digitalRead(lineFollowSensor2), LFSensor[3] =digitalRead(lineFollowSensor3), LFSensor[4] =digitalRead(lineFollowSensor4);farRightSensor =analogRead(farRightSensorPin);farLeftSensor =analogRead(farLeftSensorPin); 

    Follower Line Robot 프로젝트에서 본 것처럼 5개의 센서를 사용하면 라인 위의 로봇 위치를 제어하는 ​​데 도움이 되는 "오류 변수"를 생성할 수 있습니다. 또한 로봇이 선을 따라가는 경우 "모드"라는 변수가 정의에 사용됩니다. , 연속선 위 , 교차로 또는 줄 없음 조금도.

    이 변수 "모드 "는 "맨 왼쪽/오른쪽과도 함께 사용됩니다. " 센서입니다. 표현을 위해 3가지 가능한 상태를 가진 맨 왼쪽 및 오른쪽 센서를 고려해 보겠습니다.

    <울>
  • H(THRESHOLD보다 높음),
  • <울>
  • L(THRESHOLD보다 작음) 및
  • <울>
  • X(관련 없음).
  • 디지털 출력의 경우 일반적으로 0, 1이며 X도 소개합니다.

    <울>
  • H 0 X X X X L ==> 모드 =RIGHT_TURN; 오류 =0; (위 이미지의 예 참조)
  • <울>
  • L X X X X 0 H ==> 모드 =LEFT_TURN; 오류 =0;
  • <울>
  • X 0 0 0 0 0 X ==> 모드 =NO_LINE; 오류 =0;
  • <울>
  • H 0 0 0 0 1 H ==> 모드 =FOLLOWING_LINE; 오류 =4;
  • <울>
  • H 0 0 0 1 1 H ==> 모드 =FOLLOWING_LINE; 오류 =3;
  • <울>
  • H 0 0 0 1 0 H ==> 모드 =FOLLOWING_LINE; 오류 =2;
  • <울>
  • H 0 0 1 1 0 H ==> 모드 =FOLLOWING_LINE; 오류 =1;
  • <울>
  • H 0 0 1 0 0 H ==> 모드 =FOLLOWING_LINE; 오류 =0;
  • <울>
  • H 0 1 1 0 0 H ==> 모드 =FOLLOWING_LINE; 오류 =-1;
  • <울>
  • H 0 1 0 0 0 H ==> 모드 =FOLLOWING_LINE; 오류 =-2
  • <울>
  • H 1 1 0 0 0 H ==> 모드 =FOLLOWING_LINE; 오류 =-3;
  • <울>
  • H 1 0 0 0 0 H ==> 모드 =FOLLOWING_LINE; 오류 =-4;
  • <울>
  • X 1 1 1 1 1 X ==> 모드 =CONT_LINE; 오류 =0;
  • 따라서 함수에서 위의 논리를 구현합니다.

    readLFSsensors() 무효 

    "mode 변수를 반환합니다. " 및 "오류 " 이것은 프로그램 로직에서 사용될 것입니다. 프로젝트를 따르기 전에 센서의 로직을 테스트하는 것이 중요합니다. 벨로우즈 기능은 코드에 포함되어 있으며 테스트 목적으로 사용할 수 있습니다.

    void testSensorLogic(void) { Serial.print(farLeftSensor); Serial.print(" <==왼쪽 오른쪽==> "); Serial.print(farRightSensor); Serial.print(" 모드:"); Serial.print(모드); Serial.print("오류:"); Serial.println(오류);} 

    4단계:미로 풀기 - 왼손 법칙

    도입부에서 논의한 바와 같이, 대부분의 미로는 디자인이 아무리 복잡해 보일지라도 본질적으로 많은 접합부와 분기가 있는 하나의 연속 벽으로 형성되었습니다. 미로의 목표를 둘러싸고 있는 벽이 입구에서 미로의 둘레와 연결되어 있다면, 아무리 많은 우회로를 가더라도 한 손으로 벽에 접촉을 유지함으로써 미로를 해결할 수 있습니다. 이 '단순한' 미로는 "단순 연결 ."

    Wikipedia에서 검색하면 다음과 같은 사실을 알 수 있습니다.

    간단히 말해 왼손 법칙 다음과 같이 설명할 수 있습니다.

    모든 교차로와 미로 전체에서 왼손으로 왼쪽 벽을 만지십시오.

    <울>
  • 왼손을 벽에 대세요.
  • <울>
  • 앞으로 걷기 시작
  • <울>
  • 결국 미로의 끝에 도달하게 됩니다. 아마도 가장 짧고 직접적인 길을 가지 않을 수 있지만 도착하게 될 것입니다.
  • 따라서 여기서 핵심은 교차로를 식별하는 것입니다. , 위의 규칙에 따라 수강할 코스를 정의합니다. 특히 2D Maze에서는 8가지 유형의 교차로를 찾을 수 있습니다(위의 첫 번째 그림 참조).

    그림을 보면 교차로에서 가능한 조치는 다음과 같다는 것을 알 수 있습니다.

    1. "십자가에서 ":

    <울>
  • 왼쪽으로 이동 또는
  • <울>
  • 오른쪽으로 이동, 또는
  • <울>
  • 직진
  • 2. "T ":

    <울>
  • 왼쪽으로 이동 또는
  • <울>
  • 오른쪽으로 이동
  • 3. "오른쪽만 ":

    <울>
  • 오른쪽으로 이동
  • 4. "왼쪽 전용 ":

    <울>
  • 왼쪽으로 이동
  • 5. "직진 또는 좌 ":

    <울>
  • 왼쪽으로 이동 또는
  • <울>
  • 직진
  • 6. "스트레이트 또는 라이트에서 ":

    <울>
  • 오른쪽으로 이동, 또는
  • <울>
  • 직진
  • 7. "막다른 골목에서 ":

    <울>
  • 돌아가기("U 턴")
  • 8. "미로 끝에서 ":

    <울>
  • 중지
  • 그러나 "왼손 법칙"을 적용하면 작업이 각각 하나의 옵션으로 축소됩니다.

    <울>
  • "십자가"에서:왼쪽으로 이동
  • <울>
  • "T"에서:왼쪽으로 이동
  • <울>
  • "오른쪽만"에서:오른쪽으로 이동
  • <울>
  • "왼쪽만"에서:왼쪽으로 이동
  • <울>
  • "직진 또는 좌회전"에서:왼쪽으로 이동
  • <울>
  • "직진 또는 우회전"에서:직진
  • <울>
  • "막다른 골목"에서:뒤로 돌아가기("U턴")
  • <울>
  • "미로의 끝"에서:정지
  • 거의 다 왔습니다! "진정하세요!"

    로봇이 "막다른 골목"이나 "미로의 끝"에 도달하면 모호한 상황이 존재하지 않기 때문에 쉽게 식별할 수 있습니다. 문제는 로봇이 예를 들어 "선"을 찾을 때입니다. 선은 "십자가"(1) 또는 "T"(2)가 될 수 있기 때문입니다. 또한 "좌회전 또는 우회전"에 도달하면 단순 회전(옵션 3 또는 4) 또는 직진 옵션(5 또는 6)이 될 수 있습니다. 로봇이 어떤 유형의 교차로인지 정확히 알아내려면 추가 단계를 수행해야 합니다. 로봇은 "추가 인치"를 실행하고 다음에 무엇이 있는지 확인해야 합니다(예를 들어 위의 두 번째 그림 참조).

    따라서 흐름 측면에서 가능한 작업은 이제 다음과 같이 설명할 수 있습니다.

    1. "막다른 길"에서:

    <울>
  • 돌아가기("U턴")
  • 2. "LINE"에서:1인치 추가

    <울>
  • 라인이 있는 경우:"십자가"입니다 ==> 왼쪽으로 이동
  • <울>
  • 라인이 없는 경우:"T" ==> 왼쪽으로 이동
  • <울>
  • 다른 줄이 있는 경우:"End of Maze" ==> STOP
  • 3. "우회전"에서:1인치 더 뛰기

    <울>
  • 선이 있는 경우:직선 또는 오른쪽 ==> 직선으로 이동
  • <울>
  • 라인이 없는 경우:Right Only ==> RIGHT로 이동
  • 4. "좌회전"에서:1인치 더 뛰기

    <울>
  • 선이 있는 경우:직선 또는 LEFT ==> LEFT로 이동
  • <울>
  • 라인이 없는 경우:LEFT 전용 ==> LEFT로 이동
  • 실제로 "LEFT TURN"의 경우 어쨌든 LEFT를 선택하기 때문에 테스트를 건너뛸 수 있습니다. 나는 명확성을 위해서만 더 일반적인 설명을 남겼습니다. 실제 코드에서는 이 테스트를 건너뛸 것입니다. 위의 세 번째 사진은 테스트 목적으로 사용되는 실험실 바닥에 있는 매우 간단한 미로를 보여줍니다.

    5단계:Arduino 코드에서 "Left Hand on the Wall" 알고리즘 구현

    readLFSsensors()가 있으면 추가 2개의 센서를 포함하도록 수정된 함수, 마지막 단계에 설명된 대로 알고리즘을 실행하도록 루프 함수를 다시 작성할 수 있습니다.

    <사전><코드>무효 루프(){ readLFSsensors(); 스위치(모드) { case NO_LINE:motorStop(); goAndTurn (왼쪽, 180); 부서지다; 사례 CONT_LINE:runExtraInch(); readLFS센서(); if (모드 ==CONT_LINE) mazeEnd(); 그렇지 않으면 goAndTurn(왼쪽, 90); // 또는 "T" 또는 "Cross"). 두 경우 모두 LEFT break로 이동합니다. 사례 RIGHT_TURN:runExtraInch(); readLFS센서(); if (모드 ==NO_LINE) goAndTurn (오른쪽, 90); 부서지다; 케이스 LEFT_TURN:goAndTurn(LEFT, 90); 부서지다; 경우 FOLLOWING_LINE:다음 라인(); 부서지다; }}

    여기에 몇 가지 새로운 기능이 나타납니다.

    <울>
  • followingLine() 다음 줄 로봇에서 사용되는 것과 동일합니다. 여기서 한 줄만 따라오는 경우 calculatePID() <코드>; PID 값에 따라 모터 제어:motorPIDcontrol();
  • <울>
  • runExtraInch(): 로봇을 조금 앞으로 밀어줍니다. 로봇이 얼마나 실행될지는 모터 정지 명령을 내리기 전에 지연 기능에서 사용하는 시간에 따라 달라집니다.
  • <사전><코드>무효 runExtraInch(무효){ motorPIDcontrol(); 지연(extraInch); 모터스톱();} <울>
  • goAndTurn(방향, 각도): 이 특수 기능은 교차로 유형을 인식하자마자 로봇을 돌릴 수 없기 때문에 중요합니다. 회전할 때 "축을 중심으로 회전"하므로 90o 이동하고 라인을 계속 따라가려면 바퀴의 중심이 교차점의 중심과 정렬되어야 하는 차동 로봇을 투영했음을 기억하십시오. 센서 라인이 도끼보다 앞서면 로봇을 앞으로 움직여 정렬해야 합니다. 시간 상수 adjGoAndTurn 도끼와 센서 선 사이의 거리에 따라 조정해야 합니다("d "), 바퀴의 속도 및 크기(그림은 위의 그림 참조).
  • void goAndTurn(int 방향, int도){ motorPIDcontrol(); 지연(adjGoAndTurn); motorTurn(방향, 도);} 

    이 시점에서 로봇은 사실 "미로를 풀고 있다"! "First Pass"를 마치면 됩니다. 미로 안에서 어디서 시작하든 상관없이 항상 끝에 도달할 것입니다.

    Bellow, 프로젝트의 이 단계에 대한 테스트:

    6단계:경로 저장

    위의 사진과 같은 예를 생각해 봅시다. 선택한 시작 지점에서 로봇은 미로의 끝에 도달하기 전에 15개의 교차로를 찾습니다.

    <울>
  • 왼쪽(L)
  • <울>
  • 뒤로(B)
  • <울>
  • 왼쪽(L)
  • <울>
  • 왼쪽(L)
  • <울>
  • 왼쪽(L)
  • <울>
  • 뒤로(B)
  • <울>
  • 스트레이트(S)
  • <울>
  • 뒤로(B)
  • <울>
  • 왼쪽(L)
  • <울>
  • 왼쪽(L)
  • <울>
  • 뒤로(B)
  • <울>
  • 스트레이트(S)
  • <울>
  • 왼쪽(L)
  • <울>
  • 왼쪽(L)
  • <울>
  • 종료
  • 이러한 교차점에서 수행해야 하는 것은 발생하는 동일한 순서로 수행된 각 작업을 정확히 저장하는 것입니다. 이를 위해 로봇이 이동한 경로를 저장할 새 변수(배열)를 생성해 보겠습니다.

    문자 경로[100] =" "; 

    배열과 함께 사용할 2개의 인덱스 변수도 생성해야 합니다.

    <사전><코드>부호 없는 문자 pathLength =0; // 경로의 길이 pathIndex =0; // 특정 배열 요소에 도달하는 데 사용됩니다.

    따라서 그림에 표시된 예제를 실행하면 다음과 같이 끝납니다.

    경로 =[LBLLLBSBLLBSLL]및 pathLengh =14 

    7단계:경로 단순화(최적화)

    우리의 예로 돌아가자. 첫 번째 교차로 그룹을 살펴보면 첫 번째 왼쪽 분기가 실제로 "막다른 골목"이라는 것을 깨달았습니다. 따라서 "왼쪽-뒤-왼쪽" 대신 로봇이 첫 번째 교차로에서만 직진하기만 하면 많은 에너지가 소모됩니다. 시간을 절약할 수 있습니다! 즉, 시퀀스 "LBL"은 실제로 "S"와 동일합니다. 이것이 바로 전체 경로를 최적화할 수 있는 방법입니다. "U 턴"이 사용되는 모든 가능성을 분석하면 이 "U-Turn"("B")이 나타나는 3개의 교차로 집합("xBx")은 하나만으로 줄일 수 있습니다.

    위는 하나의 예일 뿐이며, 아래에서 전체 가능성 목록을 찾을 수 있습니다(시도).

    <울>
  • LBR =B
  • <울>
  • LBS =R
  • <울>
  • RBL =B
  • <울>
  • SBL =R
  • <울>
  • SBS =B
  • <울>
  • LBL =S
  • 전체 경로나 예를 들면 다음과 같이 줄일 수 있습니다.

    <사전><코드>경로 =[LBLLLBSBLLBSLL] ==> LBL =경로 =[SLLBSBLLBSLL] ==> LBS =R경로 =[SLRBLLBSLL] ==> RBL =B경로 =[SLBLBSLL] ==> LBL =경로 =[SSBSLL ] ==> SBS =B경로 =[SBLL] ==> SBL =R경로 =[RL]

    놀라운! 예를 보면 로봇이 첫 번째 교차로에서 오른쪽으로 이동한 다음 왼쪽으로 이동하면 최단 경로로 미로의 끝에 도달하게 됩니다!

    Maze Solver 전체 코드의 첫 번째 경로는 mazeSolve() 함수에 통합됩니다. . 이 함수는 실제로 이전에 사용된 loop() 함수이지만 저장 및 경로 최적화의 모든 단계를 통합합니다. 첫 번째 경로가 끝나면 path[] 배열이 최적화된 경로를 갖게 됩니다. 새로운 변수가 도입되었습니다:

    unsigned int status =0; // 풀기 =0; 미로 끝에 도달 =1 

    첫 번째 경로 기능 아래:

    void mazeSolve(void){ while (!status) { readLFSsensors(); 스위치(모드) { case NO_LINE:motorStop(); goAndTurn (왼쪽, 180); recIntersection('B'); 부서지다; 사례 CONT_LINE:runExtraInch(); readLFS센서(); if (모드 !=CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // 또는 "T" 또는 "Cross"). 두 경우 모두 LEFT로 이동합니다. else mazeEnd(); 부서지다; 사례 RIGHT_TURN:runExtraInch(); readLFS센서(); if (모드 ==NO_LINE) {goAndTurn (오른쪽, 90); recIntersection('R');} 그렇지 않으면 recIntersection('S'); 부서지다; 케이스 LEFT_TURN:goAndTurn(LEFT, 90); recIntersection('L'); 부서지다; 경우 FOLLOWING_LINE:다음 라인(); 부서지다; } }}  

    여기에 recIntersection(방향)이라는 새로운 기능이 도입되었습니다. 이 함수는 교차점을 저장하고 다른 함수 simplifyPath()를 호출하는 데 사용됩니다. , 그러면 이전에 본 것처럼 "U-Turn"이 포함된 3개의 교차로 그룹이 줄어듭니다.

    void recIntersection(char direction){ path[pathLength] =방향; // 경로 변수에 교차점을 저장합니다. 경로 길이 ++; 단순화 경로(); // 학습된 경로를 단순화합니다.} 

    simplifyPath(에 대한 크레딧 ) 기능은 Patrick McCabe에게 코드 해결 경로에 대한 것입니다(자세한 내용은 https://patrickmccabemakes.com 참조)! 경로 단순화의 전략은 시퀀스 xBx를 만날 때마다 막다른 골목을 잘라내어 단순화할 수 있다는 것입니다. 예:LBL ==> S 예에서 보았듯이

    void simplePath(){ // 마지막에서 두 번째 회전이 'B'인 경우에만 경로를 단순화 if(pathLength <3 || path[pathLength-2] !='B') return; 정수 총각 =0; 정수 나; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; 부서지다; 경우 'L':totalAngle +=270; 부서지다; 경우 'B':totalAngle +=180; 부서지다; } } // 각도를 0에서 360도 사이의 숫자로 가져옵니다. 총각 =총각 % 360; // 모든 턴을 단일 턴으로 바꿉니다. switch(totalAngle) { 경우 0:경로[경로길이 - 3] ='S'; 부서지다; 사례 90:경로[경로길이 - 3] ='R'; 부서지다; 사례 180:경로[경로길이 - 3] ='B'; 부서지다; 사례 270:경로[경로길이 - 3] ='L'; 부서지다; } // 이제 경로가 두 단계 짧아졌습니다. 경로 길이 -=2; }  

    8단계:두 번째 패스:최대한 빨리 미로를 푸세요!

    메인 프로그램:loop() 다음과 같이 간단합니다.

    void 루프() { ledBlink(1); readLFS센서(); 미로솔브(); // 미로를 풀기 위한 첫 번째 패스 ledBlink(2); while (digitalRead(buttonPin) { } pathIndex =0; status =0; mazeOptimization(); // 두 번째 패스:미로를 최대한 빠르게 실행 ledBlink(3); while (digitalRead(buttonPin) { } mode =STOPPED; status =0; // 첫 번째 패스 pathIndex =0; pathLength =0;} 

    따라서 First Pass가 종료되면 우리가 해야 할 일은 로봇에 최적화된 경로 배열만 공급하는 것입니다. 실행이 시작되고 교차점이 발견되면 path[]에 저장된 내용을 기반으로 수행할 작업을 정의합니다. .

    mazeOptimization 무효화(void){ while (!status) { readLFSsensors(); 스위치(모드) { case FOLLOWING_LINE:followingLine(); 부서지다; 경우 CONT_LINE:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn(경로[경로인덱스]); pathIndex++;} 중단; 케이스 LEFT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn(경로[경로인덱스]); pathIndex++;} 중단; 경우 RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn(경로[경로인덱스]); pathIndex++;} 중단; } } } 

    수행할 작업을 명령하기 위해 새로운 기능 mazeTurn(path[]) 생성되었습니다. mazeTurn(경로[]) 기능

    void mazeTurn (char dir) { switch(dir) { case 'L':// 좌회전 goAndTurn (LEFT, 90); 부서지다; case 'R':// 우회전 goAndTurn (RIGHT, 90); 부서지다; case 'B':// 되돌리기 goAndTurn (RIGHT, 800); 부서지다; case 'S':// 직진 runExtraInch(); 부서지다; }} 

    두 번째 패스가 완료되었습니다! 아래 비디오는 여기에서 작업한 전체 예제, 첫 번째 및 두 번째 패스를 보여줍니다. 이 튜토리얼에 사용된 Arduino 코드 아래:

    FV6XNJWINJ45XWM.ino F2FXS8MINJ45XX6.h FX5MHFMINJ45XX7.ino FT2S1WXINJ45XXA.ino F9IC3HQINJ45XXB.ino FU2HRXJINJ45XXV.ino

    9단계:Android를 사용하여 조정

    다음 라인 프로젝트를 위해 개발된 Android 앱도 여기에서 사용할 수 있습니다(필요한 경우 Android 앱 및 해당 코드는 Line Follower Robot - PID Control - Android Setup에서 사용할 수 있습니다. 마지막 단계에서 제시된 Arduino 코드에는 이미 다음과의 통신이 포함되어 있습니다. Android 기기에서 Android 앱을 사용하지 않으려면 코드가 "transparent이므로 문제 없습니다. ".

    "Message Received를 사용하여 로봇에서 장치로 테스트 데이터를 보내기 위해 프로젝트 중에 Android를 많이 사용했습니다 " 필드. 로봇이 올바른 각도로 회전하도록 하려면 여러 변수를 잘 정의해야 합니다. 가장 중요한 변수는 다음과 같습니다(굵게 표시된 항목은 여러 번 변경했습니다).

    <사전><코드> const int adj =0; float adjTurn =8;int adjGoAndTurn =800,THRESHOLD =150const int 거듭제곱 =250; const int iniMotorPower =250; int extraInch =200;

    10단계:결론

    이것은 인공 지능이 있는 라인 팔로워 로봇의 잠재력을 탐구하는 복잡한 프로젝트의 두 번째이자 마지막 부분입니다 (AI) 미로를 풀기 위해 간단한 개념을 사용했습니다.

    저는 AI가 아닙니다. 전문가이고 웹에서 얻은 일부 정보를 기반으로 로봇인 Rex가 하는 미로를 해결하는 것이 AI의 응용 프로그램으로 간주될 수 있음을 이해했습니다. 아래 2가지 소스를 살펴보겠습니다.

    위키피디아에서:

    또는 이 대학 논문에서:"Freeduino 및 LSRB 알고리즘을 사용한 미로 해결 로봇 IJMER(International Journal of Modern Engineering Research)"

    이 프로젝트의 업데이트된 파일은 GITHUB에서 찾을 수 있습니다. 다른 사람들이 전자 제품, 로봇, Arduino 등에 대해 더 많이 배울 수 있도록 기여할 수 있기를 바랍니다. 더 많은 자습서를 보려면 제 블로그:MJRoBot.org

    를 방문하세요.

    세계 남쪽에서 온 살루도스!

    감사합니다

    마르셀로

    <섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

    코드

    <울>
  • 코드 스니펫 #1
  • 코드 스니펫 #4
  • 코드 스니펫 #5
  • 코드 스니펫 #6
  • 코드 스니펫 #7
  • 코드 스니펫 #8
  • 코드 스니펫 #12
  • 코드 스니펫 #13
  • 코드 스니펫 #14
  • 코드 스니펫 #15
  • 코드 스니펫 #16
  • 코드 스니펫 #17
  • 코드 스니펫 #1일반 텍스트
    <사전> const int lineFollowSensor0 =12; //디지털 입력 사용 const int lineFollowSensor1 =18; //아날로그 핀 A4를 디지털 입력으로 사용 const int lineFollowSensor2 =17; //아날로그 핀 A3을 디지털 입력으로 사용 const int lineFollowSensor3 =16; //아날로그 핀 A2를 디지털 입력으로 사용 const int lineFollowSensor4 =19; //아날로그 핀 A5를 디지털 입력으로 사용 const int farRightSensorPin =0; //아날로그 핀 A0const int farLeftSensorPin =1; //아날로그 핀 A1
    코드 스니펫 #4일반 텍스트
    LFSensor[0] =digitalRead(lineFollowSensor0);LFSensor[1] =digitalRead(lineFollowSensor1);LFSensor[2] =digitalRead(lineFollowSensor2);LFSensor[3] =digitalRead(lineFollowSensor3);LFSensor[4] =digitalRead( lineFollowSensor4);farRightSensor =analogRead(farRightSensorPin);farLeftSensor =analogRead(farLeftSensorPin);
    코드 스니펫 #5일반 텍스트
     무효 testSensorLogic(void) { Serial.print(farLeftSensor); Serial.print(" <==왼쪽 오른쪽==> "); Serial.print(farRightSensor); Serial.print(" 모드:"); Serial.print(모드); Serial.print("오류:"); Serial.println(오류);}
    코드 스니펫 #6일반 텍스트
    <사전>무효 루프(){ readLFSsensors(); 스위치(모드) { case NO_LINE:motorStop(); goAndTurn (왼쪽, 180); 부서지다; 사례 CONT_LINE:runExtraInch(); readLFS센서(); if (모드 ==CONT_LINE) mazeEnd(); 그렇지 않으면 goAndTurn(왼쪽, 90); // 또는 "T" 또는 "Cross"). 두 경우 모두 LEFT break로 이동합니다. 사례 RIGHT_TURN:runExtraInch(); readLFS센서(); if (모드 ==NO_LINE) goAndTurn (오른쪽, 90); 부서지다; 사례 LEFT_TURN:goAndTurn(LEFT, 90); 부서지다; 경우 FOLLOWING_LINE:다음 라인(); 부서지다; }}
    코드 스니펫 #7일반 텍스트
    <사전>무효 runExtraInch(무효){ motorPIDcontrol(); 지연(extraInch); 모터스톱();}
    코드 스니펫 #8일반 텍스트
    void goAndTurn(int direction, int degrees){ motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(direction, degrees);}
    Code snippet #12Plain text
    void mazeSolve(void){ while (!status) { readLFSsensors(); switch (mode) { case NO_LINE:motorStop(); goAndTurn (LEFT, 180); recIntersection('B'); 부서지다; case CONT_LINE:runExtraInch(); readLFSsensors(); if (mode !=CONT_LINE) {goAndTurn (LEFT, 90); recIntersection('L');} // or it is a "T" or "Cross"). In both cases, goes to LEFT else mazeEnd(); 부서지다; case RIGHT_TURN:runExtraInch(); readLFSsensors(); if (mode ==NO_LINE) {goAndTurn (RIGHT, 90); recIntersection('R');} else recIntersection('S'); 부서지다; case LEFT_TURN:goAndTurn (LEFT, 90); recIntersection('L'); 부서지다; case FOLLOWING_LINE:followingLine(); 부서지다; } }}
    Code snippet #13Plain text
    void recIntersection(char direction){ path[pathLength] =direction; // Store the intersection in the path variable. pathLength ++; simplifyPath(); // Simplify the learned path.}
    Code snippet #14Plain text
    void simplifyPath(){ // only simplify the path if the second-to-last turn was a 'B' if(pathLength <3 || path[pathLength-2] !='B') return; int totalAngle =0; 정수 나; for(i=1;i<=3;i++) { switch(path[pathLength-i]) { case 'R':totalAngle +=90; 부서지다; case 'L':totalAngle +=270; 부서지다; case 'B':totalAngle +=180; 부서지다; } } // Get the angle as a number between 0 and 360 degrees. totalAngle =totalAngle % 360; // Replace all of those turns with a single one. switch(totalAngle) { case 0:path[pathLength - 3] ='S'; 부서지다; case 90:path[pathLength - 3] ='R'; 부서지다; case 180:path[pathLength - 3] ='B'; 부서지다; case 270:path[pathLength - 3] ='L'; 부서지다; } // The path is now two steps shorter. pathLength -=2; } 
    Code snippet #15Plain text
    void loop() { ledBlink(1); readLFSsensors(); mazeSolve(); // First pass to solve the maze ledBlink(2); while (digitalRead(buttonPin) { } pathIndex =0; status =0; mazeOptimization(); // Second Pass:run the maze as fast as possible ledBlink(3); while (digitalRead(buttonPin) { } mode =STOPPED; status =0; // 1st pass pathIndex =0; pathLength =0;}
    Code snippet #16Plain text
    void mazeOptimization (void){ while (!status) { readLFSsensors(); switch (mode) { case FOLLOWING_LINE:followingLine(); 부서지다; case CONT_LINE:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case LEFT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; case RIGHT_TURN:if (pathIndex>=pathLength) mazeEnd (); else {mazeTurn (path[pathIndex]); pathIndex++;} break; } } }
    Code snippet #17Plain text
    void mazeTurn (char dir) { switch(dir) { case 'L':// Turn Left goAndTurn (LEFT, 90); 부서지다; case 'R':// Turn Right goAndTurn (RIGHT, 90); 부서지다; case 'B':// Turn Back goAndTurn (RIGHT, 800); 부서지다; case 'S':// Go Straight runExtraInch(); 부서지다; }}
    깃허브
    https://github.com/Mjrovai/MJRoBot-Maze-Solverhttps://github.com/Mjrovai/MJRoBot-Maze-Solver

    회로도

    z7IdLkxL1J66qOtphxqC.fzz

    제조공정

    1. Raspberry Pi 및 Bridge Shield를 사용하는 로봇
    2. Raspberry Pi를 사용한 제스처 제어 로봇
    3. 라즈베리 파이를 사용하는 Wi-Fi 제어 로봇
    4. KINECT 및 RASPBERRY PI를 사용한 SONBI 로봇 인간 탐지
    5. Bosch, Industry 4.0에 인공 지능 추가
    6. 인공 지능은 허구입니까 아니면 일시적입니까?
    7. 인공 지능이 쿠버네티스의 엄청난 부스트
    8. 로봇이 터치로 물체를 인식하도록 돕는 인공 지능
    9. 인공 지능을 사용하여 삼림 벌채 추적
    10. 인공 지능 로봇