查看: 6603|回复: 79
|
邀请各位设计无线小车
[复制链接]
|
|
楼主 |
发表于 18-2-2014 12:37 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:37 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:38 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:38 AM
|
显示全部楼层
Arduino代码- ////////// blink LED variables
- #define BLINK_LED_PIN 13 //the number of the LED pin
- int ledState = LOW; //ledState used to set the LED
- long previousMillis = 0; //will store last time LED was updated
- long interval = 500; //interval at which to blink (milliseconds)
- ////////// motor variables
- #define MOTOR_ENABLE_PWM_PIN 6 // H-bridge enable (pin 1 & pin 9)
- #define LEFT_MOTOR_LOGIC1_PIN 7 // H-bridge leg 1 (pin 2, 1A)
- #define LEFT_MOTOR_LOGIC2_PIN 8 // H-bridge leg 2 (pin 7, 2A)
- #define RIGHT_MOTOR_LOGIC1_PIN 9 // H-bridge leg 1 (pin 10, 3A)
- #define RIGHT_MOTOR_LOGIC2_PIN 10 // H-bridge leg 2 (pin 15, 4A)
- #define CAR_HORN_PIN 2
- #define HEADLIGHTS_PIN 3
- #define INITIAL_SPEED 20
- #define DELAY_BETWEEN_MOTOR_CHANGE_DIRECTION 200
- int Current_speed, speeds = 0;
- ////////// use to determine car action
- #define FORWARD 1
- #define LEFT 2
- #define STOP 3
- #define RIGHT 4
- #define BACKWARD 5
- byte car_action, previous_car_action = STOP;
- ////////// serial variables
- boolean Rxenable = false;
- String inputString = ""; //a string to hold incoming data
- boolean stringComplete = false; //whether the string is complete
- void setup()
- {
- // set the digital pin as output:
- pinMode(BLINK_LED_PIN, OUTPUT);
- pinMode(LEFT_MOTOR_LOGIC1_PIN, OUTPUT);
- pinMode(LEFT_MOTOR_LOGIC2_PIN, OUTPUT);
- pinMode(MOTOR_ENABLE_PWM_PIN, OUTPUT);
- pinMode(RIGHT_MOTOR_LOGIC1_PIN, OUTPUT);
- pinMode(RIGHT_MOTOR_LOGIC2_PIN, OUTPUT);
- pinMode(CAR_HORN_PIN, OUTPUT);
- pinMode(HEADLIGHTS_PIN, OUTPUT);
- Serial.begin(9600);
- Serial.println("Wifi RC car");
- car_stop();
- }
- void loop()
- {
- blinkLED(); //blink LED every second
-
- if (stringComplete) {
- inputString.toUpperCase();
- process_command();
- inputString = ""; //clear the string:
- stringComplete = false;
- Rxenable = false;
- }
- }//end loop
- void process_command()
- {
- char command=inputString[1];
- switch (command) {
- case '2':
- car_forward();
- break;
- case '3':
- car_headLight();
- break;
- case '4':
- car_left();
- break;
- case '5':
- car_stop();
- break;
- case '6':
- car_right();
- break;
- case '7':
- car_horn();
- break;
- case '8':
- car_backward();
- break;
- case 'S':
- //extract from first character until end
- //eg. if inputString=@S20 then speeds=20
- speeds = stringToInt(inputString.substring(2));
- car_speed();
- break;
- }
- }
- void load_car_speed()
- {
- //if the car is not moving then run the car with INITIAL_SPEED
- if (previous_car_action == STOP) {
- speeds = INITIAL_SPEED;
- car_speed();
- }
-
- if ((car_action == FORWARD && previous_car_action == BACKWARD)
- || (car_action == BACKWARD && previous_car_action == FORWARD)
- || (car_action == LEFT && previous_car_action == RIGHT)
- || (car_action == RIGHT && previous_car_action == LEFT))
- {
- leftMotor_stop();
- rightMotor_stop();
- delay(DELAY_BETWEEN_MOTOR_CHANGE_DIRECTION);
- }
- previous_car_action = car_action;
- }
- void car_stop()
- {
- car_action = STOP;
- leftMotor_stop();
- rightMotor_stop();
- }
- void car_forward()
- {
- car_action = FORWARD;
- load_car_speed();
- leftMotor_run();
- rightMotor_run();
- }
- void car_backward()
- {
- car_action = BACKWARD;
- load_car_speed();
- leftMotor_backward();
- rightMotor_backward();
- }
- void car_left()
- {
- car_action = LEFT;
- load_car_speed();
- rightMotor_run();
- leftMotor_stop();
- }
- void car_right()
- {
- car_action = RIGHT;
- load_car_speed();
- leftMotor_run();
- rightMotor_stop();
- }
- void car_horn()
- {
- // Serial.print("Horn");
- if (digitalRead(CAR_HORN_PIN)==LOW) {
- digitalWrite(CAR_HORN_PIN, HIGH);
- } else {
- digitalWrite(CAR_HORN_PIN, LOW);
- }
- }
- void car_headLight()
- {
- // Serial.print("Head Light");
- if (digitalRead(HEADLIGHTS_PIN)==LOW) {
- digitalWrite(HEADLIGHTS_PIN, HIGH);
- } else {
- digitalWrite(HEADLIGHTS_PIN, LOW);
- }
- }
- void car_speed()
- {
- Current_speed = speeds;
- int speeds_mapped = map(Current_speed, 0, 100, 80, 255);
- analogWrite(MOTOR_ENABLE_PWM_PIN, speeds_mapped);
- }
- //////////////////////// leftt motor
- void leftMotor_run()
- {
- digitalWrite(LEFT_MOTOR_LOGIC1_PIN, LOW);
- digitalWrite(LEFT_MOTOR_LOGIC2_PIN, HIGH);
- }
- void leftMotor_backward()
- {
- digitalWrite(LEFT_MOTOR_LOGIC1_PIN, HIGH);
- digitalWrite(LEFT_MOTOR_LOGIC2_PIN, LOW);
- }
- void leftMotor_stop()
- {
- digitalWrite(LEFT_MOTOR_LOGIC1_PIN, LOW);
- digitalWrite(LEFT_MOTOR_LOGIC2_PIN, LOW);
- }
- //////////////////////// right motor
- void rightMotor_run()
- {
- digitalWrite(RIGHT_MOTOR_LOGIC1_PIN, LOW);
- digitalWrite(RIGHT_MOTOR_LOGIC2_PIN, HIGH);
- }
- void rightMotor_backward()
- {
- digitalWrite(RIGHT_MOTOR_LOGIC1_PIN, HIGH);
- digitalWrite(RIGHT_MOTOR_LOGIC2_PIN, LOW);
- }
- void rightMotor_stop()
- {
- digitalWrite(RIGHT_MOTOR_LOGIC1_PIN, LOW);
- digitalWrite(RIGHT_MOTOR_LOGIC2_PIN, LOW);
- }
- ////////// convert string to integer
- int stringToInt(String str)
- {
- char char_string[str.length()+1];
- str.toCharArray(char_string, str.length()+1);
- return atoi(char_string);
- }
- ////////// blink LED without delay
- void blinkLED()
- {
- unsigned long currentMillis = millis();
- if(currentMillis - previousMillis > interval) {
- // save the last time you blinked the LED
- previousMillis = currentMillis;
- // if the LED is off turn it on and vice-versa:
- if (ledState == LOW)
- ledState = HIGH;
- else
- ledState = LOW;
- // set the LED with the ledState of the variable:
- digitalWrite(BLINK_LED_PIN, ledState);
- }
- }
- /*
- SerialEvent occurs whenever a new data comes in the
- hardware serial RX. This routine is run between each
- time loop() runs, so using delay inside loop can delay
- response. Multiple bytes of data may be available.
- */
- void serialEvent() {
- while (Serial.available()) {
- char inChar = (char)Serial.read(); // get the new byte:
- if (inChar == '@') { // Can start recording after [url=home.php?mod=space&uid=1968553]@[/url] symbol
- Rxenable = true;
- }
-
- if (Rxenable==true) {
- inputString += inChar; // add it to the inputString:
- // if the incoming character is a carriage return, set a flag
- // so the main loop can do something about it:
- if (inChar == '\r') {
- stringComplete = true;
- }
- }
- }
- }
复制代码 此wifi遥控小车使用了一个Arduino作为控制器,请浏览这里关于更多Arduino资料。
别让272行的Arduino代码吓着了,其实很简单也易懂。
为了方便调试,建议每个输出连接一LED,在不连接马达的情况下也能知道Arduino是否正常工作。
代码动用了一个串口作为接收指令,baud rate设为9600,所有指令必须有一Carriage Return作为结束。 自Arduino1.0版本起,serialEvent()函数就集成在IDE里了,毎一次loop都会运行 serialEvent()。当串口出现数据时,serialEvent()一直loop至接收到Carriage Return,之后资料将会保存在 inputString变数里。只要接收到一个Carriage Return,serialEvent()会跳出,接着运行process_command(),代码将会根据接收到的指令运行不同的工作,比如转左,开灯。。。。。。
当Arduino操作时,D13每隔500 milli seconds (0.5秒)闪烁一次,没有什么特别功能,只是让你知道:"嗨!我正在操作"。这个闪烁功能是由 blinkLED()负责,由于它是一个没有延迟(delay)的闪烁功能,所以它不会影响其他的代码运行。
发送到串口的指令格式必须如下:
@command\r 或者@Sx\r
数据必须由一个@起始,接下来才是command。没有@起始的数据都会作废。
command是一个单字,例:
@2 (向前跑)
@4 (转左)
@5 (停)
@Sx只是用来控制速度,x的值是0至100,0为停,100为最快速度
速度是由 analogWrite来控制D6,此输出必须支持PWM。由于它是8位分辨率,其值位于0与255之间。为了易读,我使用了map()函数,把它的范围值map成0至100。如果连接一LED至D6,你可以控制它的亮度。
\r则是Carriage Return的写法。没有接收到Carriage Return,Arduino将不会执行小汽操纵或特定工作。
喇叭(D2)与汽车(D3)双态触发(toggle),第一次开,下一次是关。
最后要捕充的是在load_car_speed()函数里(第97行)有一个INITIAL_SPEED常数 (现在设为20 PWM,其值位于0与100之间),用处是当小车停止时,接下来收到向前或向后指令,小车将会使用这速度移动。另外在第112行有一 DELAY_BETWEEN_MOTOR_CHANGE_DIRECTION 常数 ,它的用处是当马达反方向旋转时,先让马达停顿一下(现在设为200 milli seconds),用于保护马达。但我不知道是否必要!
本帖最后由 西门庆33 于 22-2-2014 07:41 PM 编辑
|
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:38 AM
|
显示全部楼层
web编程
这里的编程是基于web方面处理,好处是不须另外安装客户端应用软件就可遥控小车,且是cross platform,坏外是程序必须先安装在路由器,这将会占用路由器的flash记忆体。
在www主目录里有三个文件和三个子目录,car.html是主页,car.css是layout(skin), car.js包含一系列的function(函数)。而子目录cgi-bin里有一个名为car的文件,其作用是让路由器与Arduino沟通。代码非常简单,只有三行,如下- #!/bin/sh
- cmd=$QUERY_STRING
- echo $cmd+"\r" > /dev/ttyUSB0
复制代码
- 第二行是把QUERY_STRING 值(字串)分配给cmd,QUERY_STRING 的值是通过URL查询而取得。只有在?分隔符後的字串才会分配给QUERY_STRING。例:
- http://routerIP/cgi-bin/car?@S20
复制代码 从上面的例子,我们得知QUERY_STRING 的值是@S20
- 第三行是从URL取得的cmd(这里指QUERY_STRING)输出到dev/ttyUSB0(如果用在Ms Windows,是指COM1),
正常来讲,不用编写复杂的web图形用户界面也是可以控制这个小车的。只要打开浏览器输入如下URL格式就行了- http://routerIP/cgi-bin/car?@command
复制代码 其中routerIP是你的TL-MR3020的IP地址,这里是192.168.0.1,command则是你想要执行的指令,车转左、车转右、车打灯等等。例:
车转左- http://192.168.0.1/cgi-bin/car?@4
复制代码 车转右- http://192.168.0.1/cgi-bin/car?@6
复制代码 视频(这指令不用运行/cgi-bin/car,只是让你知道如何查看视频)- http://192.168.0.1:8080/?action=stream
复制代码 上面例子解释如何通过URL控制小车,当然,使用图形用户界面控制小车更为容易。你又要编写入程序了。
想要执行car.html主页时,只需在浏览器里输入http://yourRouterIP/car.html(yourRouterIP是指你的路由器IP地址,这里例子是192.168.0.1)就行了,下面画面将会出现。由于还没完成webcam方面安装,视频部分将是空白。
注意:
请参考二楼的WinSCP把www文件夹复制至路由器的www文件夹
在这里的web编程不需要特别的网页制作软件,使用Notepad直接编辑就行了。不过我还是要介绍一个好用,免费,轻巧的WYSIYWG(What you see is what you get)网页制作软件--kompoZer。用kompoZer打开car.html,你会看到以下画面。
使用kompoZer非常容易,自己摸索吧!
运行car.html时,它先会读取几个外部文件,如下
- ../codebase/dhtmlxcommon.js
- ../codebase/dhtmlxslider.js
- ../codebase/ext/dhtmlxslider_start.js
- ../../codebase/dhtmlxslider.css
- car.js
- car.css
其中1至4是我从网站下载的JavaScript组件,用处是建立和显示sliderbar,上面图片的sliderbar就是用这个组件建立的。
接下来是建立键盘捕捉,代码如下- <body onkeypress="return keyPressed(event)" onkeyup="keyUp(event)"
复制代码 第一次运行时将会运行autoRun()程序设背景为黑色- style="background-color: black;">
复制代码 显示webcam视频,其中192.168.0.1是路由器的IP地址,通信口是8080- <div class="img"> <a target="_parent"><img
- style="border: 0px solid ; width: 320px; height: 190px;"
- src="http://192.168.0.1:8080/?action=stream"></a></div>
复制代码 接下来是一系列的imageClick功能,点击了谋个图象就会执行相对的指令。以下代码从images文件夹里读取b2.png以便显示在屏幕,当不能完整读取b2.png文件时(原因很多,可能internet有问题), Forward 字体将会取代之。点击了这个b2.png图象,将会执行runCmd(2)- <div class="img"> <a target="_parent" href="javascript:runCmd(2)"><img
- src="images/b2.png" id="2" alt="Forward"></a>
- </div>
复制代码 imageClick的指令或者从cgi(可以是Arduino的状态)返回的值将会分配给result,然後显示在固字位置- <p class="padding"> Command: <span id="result"></span></p>
复制代码 以上所有函数呼叫(function call)都是储存在car.js里。
另外你会看到很多<div..>,这是它的属性,包括位置,字形等等。这些属性都储存在car.css- <div class="img">
- .
- .
- .
- </div>
复制代码 键盘捷径,请参考car.js的function keyPressed(e)和function keyUp(e),第10行至72行。
Keyboard
| Function
| Command
| Up Arrow
| 向前移
| @2
| Down Arrow
| 向后移
| @8
| Left Arrow
| 转左
| @4
| Right Arrow
| 转右
| @6
| Space
| 停止
| @5
| Q
| Cam (保留)
| Q
| W
| 切换车灯
| W
| A
| 喇叭
| A
| S
| 麦克风 (保留)
| S
| 1
| 速度 1
| @S20 | 2
| 速度 2
| @S40
| 3
| 速度 3
| @S60
| 4
| 速度 4
| @S70
| 5
| 速度 5
| @S100
|
本帖最后由 西门庆33 于 27-2-2014 11:28 PM 编辑
|
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:38 AM
|
显示全部楼层
Client application编程
我这个运行在Ms Windows的客户应用程序是使用Delphi 7写的,应该没人感兴趣,这里随便带过。如果你懂Visual Studio编程,请放马过来。
控制也是使用HTTP GET method, 请参考六楼的http://routerIP/cgi-bin/car?@command
如果想使用自己的设定(包括按键),可以建立一config.txt文件,把它保存在客户应用程序的文件夹里。内容如果如下- [Settings]
- StartCharacter= @
- IpAddress= 192.168.0.1
- CamPort= 8080
- 'mic
- Key1= A
- 'head light
- Key3= S
- 'horn
- Key7= Z
- 'mic
- Key9= X
- 'key for predefined speed
- Speed1= 1
- Speed2= 2
- Speed3= 3
- Speed4= 4
- Speed5= 5
复制代码 本帖最后由 西门庆33 于 28-2-2014 12:08 AM 编辑
|
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:38 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:39 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:39 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:39 AM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 12:40 AM
|
显示全部楼层
|
|
|
|
|
|
|
发表于 18-2-2014 11:34 AM
|
显示全部楼层
保留给俺作参考,顺便监督楼主不会弃楼。。。。 |
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 06:36 PM
|
显示全部楼层
可怜草泥马 发表于 18-2-2014 11:34 AM
保留给俺作参考,顺便监督楼主不会弃楼。。。。
太贪心的缘固,我现在後悔了。
不过我说了需要三年的时间来完成编写,请慢慢等待吧
|
|
|
|
|
|
|
|
发表于 18-2-2014 08:01 PM
|
显示全部楼层
太复杂了!不过为了看到裙底风光是值得的。。。。 |
评分
-
查看全部评分
|
|
|
|
|
|
|
发表于 18-2-2014 09:01 PM
|
显示全部楼层
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 09:44 PM
|
显示全部楼层
koksoon980 发表于 18-2-2014 08:01 PM
太复杂了!不过为了看到裙底风光是值得的。。。。
说复杂,並不复杂,说容易,也真不容易。还没真正开始,制作时可能会比想象中容易。
|
|
|
|
|
|
|
|
楼主 |
发表于 18-2-2014 10:11 PM
|
显示全部楼层
weitao 发表于 18-2-2014 09:01 PM
LZ为何不用TX2B/RX2B?
TX2B/RX2B真的不错,不用编程,也能完成遥控。只需要设计一个发送器和一个接收器就行了。不过它是不适合用来制作这类遥控小车。
- 不能连接Webcam。最重要的一环:裙底风光也就不能实现了
- 不能通过PC遥控,包括手机(Android/IOS)。也没有图形用户界面。
- 没有时速控制
- 距离有限(WIFI有很多方式增强信号和增加距离)
- 不能通过internet遥控
- TX-2B只能有五个频道控制
- 扩展空间(功能)有限
路由器遥控小车还有许多功能。夸张点,因为这个路由器已经变成了一部嵌入式电脑
本帖最后由 西门庆33 于 18-2-2014 10:19 PM 编辑
|
|
|
|
|
|
|
|
发表于 18-2-2014 10:30 PM
|
显示全部楼层
西门庆33 发表于 18-2-2014 10:11 PM
TX2B/RX2B真的不错,不用编程,也能完成遥控。只需要设计一个发送器和一个接收器就行了。不过它是不适合用 ...
LZ不好意思!!!我错了!只好期待你的作品!!!
|
|
|
|
|
|
|
|
发表于 19-2-2014 10:11 AM
|
显示全部楼层
单是这个两轮驱动就是一个大项目了。。。,。
你的2轮平衡, 跌倒后能自己爬起来, 这个要先独立建立起来先。。
简单的做法。。
一台智能手机, 能数据上网的,含前后Camera 的, 含GPS 的。
透过USB 扩展I/O, 或者蓝牙扩展I/O, 以便发信号去你的 2 轮车控制板, 方向控制, 速度控制, 控制你的horn 开灯 有的没的。。。或者喇叭, 你要说话, 传回mic 都可以吧。。
你需要编写一个Android Apps,就可以轻松的透过互联网传回画面, 和控制2轮车。
喜欢轻度折腾的做法:
如果不要用Smart phone, 可以用RPI (RasberryPi) 或CB (CubieBoard) 这类的主板, 加3G GSM 模块, Webcam 之类的, 再追加GPS定位。
镜头要有2个Servor motor 控制角度/高度, 方向, 然后你要编写Linux/Android 下能运行的程序。
非折腾不喜的做法:
我理解, 也些人喜欢折腾的, 他们认为折腾才有乐趣。。
那么, 就尽量不要用模块了。。。所有的东西,控制, 通讯都老老实实的用MCU 去做吧。。。
嗯。。到猴年马月时, 估计就做出来了。。。
在PC/手机 控制方面, 能上网Web base 控制就可以了。。
因为含GPS, 使用3G 数据联网, 只要有信号, 跨国控制都可以。。
GPS 是防丢, 被人绑架/捡去了还有机会找回。。走丢了,迷路了, 还有可能自己导航回来 (可能你又要多1年的时间去做这个功能)
其实, 你要的都能做到。。即使是2轮车的机械部分,相信都有模块, 和很多参考资料了。
最后, 也是最重要的, 你的预算多少钱。。??
多少钱啊?
|
|
|
|
|
|
|
| |
本周最热论坛帖子
|