本人也是零基础进行Arduino学习的初学者,做小车期间遇到过很多困难,在此写下整个小车的制作流程以及遇到的问题与解决方案,希望对后来者有所帮助。在制作期间参考了许多文章得到了大佬的指点,以及下面展示成果如建模等是由朋友做的,在此一并表示感谢。
总结来说,小车基于Arduino UNO单片机,通过并联L298N电机驱动板实现八输出(因为同时控制四个轮的正反转需要八个变量)来控制小车除前后直线移动,左拐右拐之外,还可以顺时针逆时针原地自旋以及向左向右横向平移。并加载Arduino扩展板实现循迹、寻光、避障、蓝牙遥控和无源蜂鸣器报警功能。
首先整体结构为双层亚克力板,如图其下层承载四个TT电机、三个循迹模块传感器以及两个红外避障传感器和18650电池组,上层承载Arduino开发板与拓展板、两个L298N电机驱动板、蜂鸣器、蓝牙模块和两个循光传感器。两层亚克力板间采用铜柱与螺栓联接。
车体亚克力板部分以及循迹、寻光、红外避障传感器、蓝牙模块、tt电机都是由淘宝买的套件小车的产品,不需要自己配置散件。可以购买时仔细阅读套件清单或者直接向商家问个清楚,商家都很有耐心的。(依次为红外避障、寻光、循迹传感器)



麦克纳姆轮是需要单独购买的,小车套件里面的是四个普通的轮子。电池也是单独购买的两节18650可充电锂电池,电机驱动板是两个L298N,此外还单独购买了一个无源蜂鸣器。这些单独购买的也可以很容易在淘宝上找到,但是有一些购买细节,这些在下文会单独说明。

需要说明的是,虽然Arduino需要5V的供电电压,但是并不需要再额外配置一个5V电压源,因为两节18650接入电机驱动板供电后,电机驱动板可以有一路降压输出5V给Arduino板子。
对于蓝牙模块接线,除了蓝牙模块的+-与Arduino板子的+-对应以外。蓝牙模块上标注RX的接口需要连接板子TX的接口,同样蓝牙TX要对应板子RX(rx是接收receive,tx是发送transport)。同样容易出错的是上传程序时需要把蓝牙模块卸下,因为其占用着Arduino板子的RX与TX,程序报错无法上传。
对于不同的套件、接入不同的传感器有不同的接线图,认清是A口还是D口,哪里是VCC哪里是GND,灵活变通即可。接线图如图所示。

电机驱动板是在Arduino主板和tt电机之间的角色,以实现对TT电机的控制。虽然只需要一个L293D就可以实现八输出,但是需要插到Arduino主板上进行供电,就因此无法再安装扩展板,也就没法安装众多的传感器(其实是可以的,但我不会,对不起)。

每个L298N有四个输入接口:IN1、IN2、IN3、IN4,用四根线和Arduino的四个Digital接口连接,以实现主板对电机驱动板的控制。每个L298N同时有四个输出接口OUT1、OUT2、OUT3、OUT4(与四个输入接口是一一对应的),因此用四根线可以分别连接两个电机的正负极,这样两个电机驱动板可以实现四个电机的正反转控制。由于电机安装方向和接线方向的影响,直接判断哪个接口可以使轮子正转也可以,单建议安装完电机后通过程序试一试来确定IN1、IN2、IN3、IN4四个接口中哪个接口输入高电平可以使轮子正转。

然后是电机驱动板之间的并联,就是电源的正极和两个电机驱动板的正极连接,从电机驱动板的5V引出一路给Arduino主板供电,电源负极和两个电机驱动板还有主板的负极连接在一起,如上图左侧电路示意图。并联的原因就是为了可以少用一个电源,减少麻烦,所以若是不想并联各自给电机驱动板配置一个电源也无所谓,记得两个电机驱动板和Arduino主板的GNG要接在一起就可以了。
对于四轮小车来说,一套麦克纳姆轮至少应含有两种不同的轮子。我们选择两种就好—两个左轮两个右轮(注意这个左右不是位于左边的轮子和位于右边的轮子的意思,是轮子的构造方向不一样)。左轮右边轮构型如图所示,所以购买的时候一定要和商家说清楚。


具体麦克纳姆轮如何实现平移以及自旋,还有轮子的安装顺序都可以看这篇文章,在此对作者表达感谢(不知道这样引用对不对)。然后控制程序可以见下文,我的控制程序更零基础(小白)一些。
有源无源可能不是没有了解过的人想像的那个意思, “源” 不是指电源,而是指振荡源。简而言之无源蜂鸣器可以自己编写一些勉强分得清在唱什么的曲调,有源的就不可以。
需要注意的是想要实现蜂鸣器唱歌需要在程序中加入头文件 pitches.h,该文件包含典型音符的所有音高值。加入头文件的方法十分容易,若是不会可以百度一下。记得把头文件和Arduino程序文件放在同一个文件夹里就好。
如果有兴趣可以看下面这篇文章。(谢谢作者!)
电源是受挫最多的地方,起初图方便用六节南孚电池作为电源,结果程序在电脑上调试时好好的,一断开USB线用电源供电就执行的晕头转向,十分玄学。后来由大佬的指点发现是因为南孚电池电流太大导致Arduino主板一直不定时重启。
所以后来更换了更为稳定的两节18650可充电锂电池,一共7.4V,问题得以解决(要是实在解决不了可以先把所有程序都调好了后上传试试,说不定就可以了呢)。

还有就是不可忽略的小车的传感器的调试环节,上传代码成功后小车依旧无法巡线无法避障,这时候可能不是代码的问题,可能是传感器输入调试没做好。网上或者淘宝商家也有一些传感器的调试方法可以借鉴,最重要的就是亲自一点点扭转传感器上的电位器一遍遍试,找到最合适的电位器位置以及最合适的传感器安装位置。(比如三个巡线传感器就可以适当离的宽一些)
最后小车还有一个非常玄学的地方,就是右侧后轮经常莫名其妙地没有指令就转起来了。即使重新接线也不行,令人困惑。没有找到根源,但是基于经验是只要按压小车前端就可以让他停止这种现象,所以用轧带将前端进行固定。
有点地方不太对,不过无伤大雅,加了一个车壳和保险杠,不过没加工出来。

GIF太大无法上传,想看看巡线、蓝牙遥控的视频可以去主页,写的时候还在审核不知道能不能过。至于避障、寻光也没必要上传了。
蓝牙遥控的app是一个朋友做的,不方便共享,不过这玩意可以在应用市场下载,就是有些广告。下面是操作命令表,也可以到程序里自己差(避障模式2的指令是p,忘记写上了):

俯看小车,车头在上将四个轮子分别编号为左1左2右1右2(程序中轮子的对应名称,这个需要根据前文所说的哪个接口接入高电平是正转自己更改程序,很简单小学二年级就学过)。
这是无源蜂鸣器播放《致爱丽丝》的功能,需要的pitches.h头文件。头文件下面是各个功能合并后的代码,可以用蓝牙来进行功能模式的切换。由于用的都是些学c语言第一课就能学完的基础语句,具体原理看代码就能很容易看懂,就无需多言了。
//pitches.h 音高定义
//pitches.h 定义了不同音符对应的不同频率
#define NOTE_B0 31 //B0 ····7
#define NOTE_C1 33 //C1 ···1
#define NOTE_CS1 35 //C1#
#define NOTE_D1 37 //D1 ···2
#define NOTE_DS1 39 //D1#
#define NOTE_E1 41 //E1 ···3
#define NOTE_F1 44 //F1 ···4
#define NOTE_FS1 46 //F1#
#define NOTE_G1 49 //G1 ···5
#define NOTE_GS1 52 //G1#
#define NOTE_A1 55 //A1 ···6
#define NOTE_AS1 58 //A1#
#define NOTE_B1 62 //B1 ···7
#define NOTE_C2 65 //C2 ··1
#define NOTE_CS2 69 //C2#
#define NOTE_D2 73 //D2 ··2
#define NOTE_DS2 78 //D2#
#define NOTE_E2 82 //E2 ··3
#define NOTE_F2 87 //F2 ··4
#define NOTE_FS2 93 //F2#
#define NOTE_G2 98 //G2 ··5
#define NOTE_GS2 104 //G2#
#define NOTE_A2 110 //A2 ··6
#define NOTE_AS2 117 //A2#
#define NOTE_B2 123 //B2 ··7
#define NOTE_C3 131 //C3 ·1
#define NOTE_CS3 139 //C3#
#define NOTE_D3 147 //D3 ·2
#define NOTE_DS3 156 //D3#
#define NOTE_E3 165 //E3 ·3
#define NOTE_F3 175 //F3 ·4
#define NOTE_FS3 185 //F3#
#define NOTE_G3 196 //G3 ·5
#define NOTE_GS3 208 //G3#
#define NOTE_A3 220 //A3 ·6
#define NOTE_AS3 233 //A3#
#define NOTE_B3 247 //B3 ·7
#define NOTE_C4 262 //C4 1(中央C大调)
#define NOTE_CS4 277 //C4#
#define NOTE_D4 294 //D4 2
#define NOTE_DS4 311 //D4#
#define NOTE_E4 330 //E4 3
#define NOTE_F4 349 //F4 4
#define NOTE_FS4 370 //F4#
#define NOTE_G4 392 //G4 5
#define NOTE_GS4 415 //G4#
#define NOTE_A4 440 //A4 6
#define NOTE_AS4 466 //A4#
#define NOTE_B4 494 //B4 7
#define NOTE_C5 523 //C5 1·
#define NOTE_CS5 554 //C5#
#define NOTE_D5 587 //D5 2·
#define NOTE_DS5 622 //D5#
#define NOTE_E5 659 //E5 3·
#define NOTE_F5 698 //F5 4·
#define NOTE_FS5 740 //F5#
#define NOTE_G5 784 //G5 5·
#define NOTE_GS5 831 //G5#
#define NOTE_A5 880 //A5 6·
#define NOTE_AS5 932 //A5#
#define NOTE_B5 988 //B5 7·
#define NOTE_C6 1047 //C6 1··
#define NOTE_CS6 1109 //C6#
#define NOTE_D6 1175 //D6 2··
#define NOTE_DS6 1245 //D6#
#define NOTE_E6 1319 //E6 3··
#define NOTE_F6 1397 //F6 4··
#define NOTE_FS6 1480 //F6#
#define NOTE_G6 1568 //G6 5··
#define NOTE_GS6 1661 //G6#
#define NOTE_A6 1760 //A6 6··
#define NOTE_AS6 1865 //A6#
#define NOTE_B6 1976 //B6 7··
#define NOTE_C7 2093 //C7 1···
#define NOTE_CS7 2217 //C7#
#define NOTE_D7 2349 //D7 2···
#define NOTE_DS7 2489 //D7#
#define NOTE_E7 2637 //E7 3···
#define NOTE_F7 2794 //F7 4···
#define NOTE_FS7 2960 //F7#
#define NOTE_G7 3136 //G7 5···
#define NOTE_GS7 3322 //G7#
#define NOTE_A7 3520 //A7 6···
#define NOTE_AS7 3729 //A7#
#define NOTE_B7 3951 //B7 7···
#define NOTE_C8 4186 //C8 1····
#define NOTE_CS8 4435 //C8#
#define NOTE_D8 4699 //D8 2····
#define NOTE_DS8 4978 //D8#
//(寻线 红外避障 蓝牙遥控 寻光)多功能程序
//***********************定义电机腳位*************************
#include "pitches.h"
int MotorLeft1R = 2;
int MotorLeft1B = 3;
int MotorLeft2R = 5;
int MotorLeft2B = 4;
int MotorRight1R = 10;
int MotorRight1B = 9;
int MotorRight2R = 6;
int MotorRight2B = 7;
// 记录曲子的音符
int melody[] = { 0, 0, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_B4, NOTE_D5, NOTE_C5,
NOTE_A4, 0, NOTE_C4, NOTE_E4, NOTE_A4, NOTE_B4, 0, NOTE_E4, NOTE_G4, NOTE_B4,
NOTE_C5, 0, NOTE_E4, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_D5, NOTE_E5, NOTE_B4, NOTE_D5, NOTE_C5,
NOTE_A4, 0, NOTE_C4, NOTE_E4, NOTE_A4, NOTE_B4, 0, NOTE_E4, NOTE_C5, NOTE_B4,
NOTE_A4};
// 音符持续时间:4为四分音符,8为八分音符
int noteDurations[] = { 4, 4, 8, 8, 8, 8, 8, 8, 8, 8,
4, 8, 8, 8, 8, 4, 8, 8, 8, 8,
4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
4, 8, 8, 8, 8, 4, 8, 8, 8, 8,
4, 4, 8, 8, 8, 8, 8, 8, 8, 8,
4};
int counter = 0;
char val;
//*************************定义寻线模块脚位************************************
const int SensorLeft = 8; //左传感器
const int SensorMiddle = 11; //中传感器
const int SensorRight = 12; //右传感器
int SL; //左传感器状态
int SM; //中传感器状态
int SR; //右传感器状态
const int barrier_left = A5; //左红外避障
const int barrier_right = A4; //右红外避障
const int followLight_left = A3; //左寻光
const int followLight_right = A2; //右寻光
//********************************************************************(SETUP)
void setup()
{
Serial.begin(9600);
pinMode(MotorLeft1R,OUTPUT);
pinMode(MotorLeft1B,OUTPUT);
pinMode(MotorLeft2R,OUTPUT);
pinMode(MotorLeft2B,OUTPUT);
pinMode(MotorRight1R,OUTPUT);
pinMode(MotorRight1B,OUTPUT);
pinMode(MotorRight2R,OUTPUT);
pinMode(MotorRight2B,OUTPUT);
pinMode(SensorLeft, INPUT); //
pinMode(SensorMiddle, INPUT);//
pinMode(SensorRight, INPUT); //
pinMode(barrier_left, INPUT);
pinMode(barrier_right, INPUT);
pinMode(followLight_left, INPUT);
pinMode(followLight_right, INPUT);
Serial.println("-----------------");
}
//******************************************************************(Void)
void advance(int a) // 前进
{
Serial.println("advance");
//直走 1的R为前,2的B为前
digitalWrite(MotorLeft1R, HIGH);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, HIGH);
digitalWrite(MotorRight1R, HIGH);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, HIGH);
delay(a * 100);
}
void right(int b) //右转
{
Serial.println("right");
//右转(普通)
digitalWrite(MotorLeft1R, HIGH);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, HIGH);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, LOW);
delay(b * 100);
}
void left(int c) //左转
{
Serial.println("left");
//左转(普通)
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, HIGH);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, HIGH);
delay(c * 100);
}
void turnR(int d) //右转 双轮
{
Serial.println("turnR");
//右转自旋(快速)
digitalWrite(MotorLeft1R, HIGH);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, HIGH);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, HIGH);
digitalWrite(MotorRight2R, HIGH);
digitalWrite(MotorRight2B, LOW);
delay(d * 100);
}
void turnL(int e) //左转 双轮
{
Serial.println("turnL");
//左转(快速)
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, HIGH);
digitalWrite(MotorLeft2R, HIGH);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, HIGH);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, HIGH);
delay(e * 100);
}
void stopp(int f) //停止
{
Serial.println("stopp");
//停止
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, LOW);
delay(f * 100);
}
void back(int g) //后退
{
Serial.println("back");
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, HIGH);
digitalWrite(MotorLeft2R, HIGH);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, HIGH);
digitalWrite(MotorRight2R, HIGH);
digitalWrite(MotorRight2B, LOW);
delay(g * 100);
}
void toR(int v) //右平移
{
Serial.println("toR");
digitalWrite(MotorLeft1R, HIGH);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, HIGH);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, HIGH);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, HIGH);
delay(v * 100);
}
void toL(int x) //左平移
{
Serial.println("toL");
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, HIGH);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, HIGH);
digitalWrite(MotorRight1R, HIGH);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, HIGH);
digitalWrite(MotorRight2B, LOW);
delay(x * 100);
}
//******************************************************************************(LOOP)
void loop()
{
SL = digitalRead(SensorLeft);
SM = digitalRead(SensorMiddle);
SR = digitalRead(SensorRight);
performCommand();
}
void performCommand()
{
if (Serial.available())
{
val = Serial.read();
}
//**********************红外避障模式
if (val == 'q') //红外壁障
{
while (1)
{
val = Serial.read();
if ((digitalRead(barrier_left)==LOW) && (digitalRead(barrier_right)==LOW))
{
back(3);//后退
}
else if (digitalRead(barrier_left)==LOW)
{
turnR(3); // 右转;
}
else if (digitalRead(barrier_right)==LOW)
{
turnL(3); // 左转;
}
else
{
advance(3);//前进
}
if (val == 'a')
{
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, LOW);
break;
}
}
}
//**********************红外避障模式2
if (val == 'p') //红外壁障
{
while (1)
{
val = Serial.read();
if ((digitalRead(barrier_left)==LOW) && (digitalRead(barrier_right)==LOW))
{
stopp(3);//停止
for(int thisNote = 0; thisNote < sizeof(melody)/sizeof(melody[0]); thisNote++)
{
// 需要根据音符类型换算音符的持续时间
int noteDuration = 1000/noteDurations[thisNote];
tone(A1, melody[thisNote], noteDuration);
// 为了能辨别出不同的音调,需要在两个音调间设置一定的延时,增加30%比较合适
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
noTone(A1);
}
}
else if (digitalRead(barrier_left)==LOW)
{
turnR(3); // 右转;
}
else if (digitalRead(barrier_right)==LOW)
{
turnL(3); // 左转;
}
else
{
advance(3);//前进
}
if (val == 'a')
{
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, LOW);
break;
}
}
}
//**********************寻光模式
if (val == 't')
{
while (1)
{
val = Serial.read();
if ((digitalRead(followLight_left)==LOW) && (digitalRead(followLight_right)==LOW))
{
advance(3);//前进
}
else if (digitalRead(followLight_left)==LOW)
{
turnL(3); // 左转;
}
else if (digitalRead(followLight_right)==LOW)
{
turnR(3); // 右转;
}
else
{
stopp(3);
}
if (val == 'a')
{
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, LOW);
break;
}
}
}
//**********************寻线模式
if (val == 'd')
{
while (1)
{
val = Serial.read();
{
SL = digitalRead(SensorLeft);
SM = digitalRead(SensorMiddle);
SR = digitalRead(SensorRight);
Serial.print(SL);
Serial.print("-");
Serial.print(SM);
Serial.print("-");
Serial.println(SR);
if (SM == HIGH)//中间传感器检测到黑线 黑线为1 白为0
{
if (SL == LOW & SR == HIGH) //左白右黑, 向右转弯
{
Serial.println("BL");
right(1); // 右转;
}
else if ( SL == HIGH & SR == LOW) // 左黑右白, 向左转弯
{
Serial.println("BR");
left(1); // 左转;
}
else // 两侧为白色,前进
{
Serial.println("Bw");
advance(1); //前进;
}
}
else // 中间传感器在白色区域
{
if (SL == LOW & SR == HIGH ) // 左白右黑, 快速右转
{
Serial.println("WL");
turnR(1);
}
else if ( SL == HIGH & SR == LOW ) // 左黑右白, 快速左转
{
Serial.println("WR");
turnL(1);
}
else // 都是白色, 保护不变
{
Serial.println("ww");
}
}
}
if (val == 'a')
{
digitalWrite(MotorLeft1R, LOW);
digitalWrite(MotorLeft1B, LOW);
digitalWrite(MotorLeft2R, LOW);
digitalWrite(MotorLeft2B, LOW);
digitalWrite(MotorRight1R, LOW);
digitalWrite(MotorRight1B, LOW);
digitalWrite(MotorRight2R, LOW);
digitalWrite(MotorRight2B, LOW);
break;
}
}
}
//**********************无源蜂鸣器
if (val == 'i') //致爱丽丝
{
while (1)
{
val = Serial.read();
// 遍历整个曲子的音符
for(int thisNote = 0; thisNote < sizeof(melody)/sizeof(melody[0]); thisNote++)
{
// 需要根据音符类型换算音符的持续时间
int noteDuration = 1000/noteDurations[thisNote];
tone(A1, melody[thisNote], noteDuration);
// 为了能辨别出不同的音调,需要在两个音调间设置一定的延时,增加30%比较合适
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
noTone(A1);
}
if (val == 'a')
{
break;
}
}
}
//蓝牙控制**********************正常遥控模式
if (val == 'f')
{ // Forward
advance(10);
}
else if (val == 'b')
{ // Backward
back(10);
}
else if (val == 'r')
{ // Right
right(10);
}
else if (val == 'l')
{ // Left
left(10);
}
else if (val == 's')
{ // Stop
stopp(10) ;
}
else if (val == 'y')
{ // Right
turnR(10);
}
else if (val == 'z')
{ // Left
turnL(10);
}
else if (val == 'v')
{ //
toR(10) ;
}
else if (val == 'x')
{ //
toL(10) ;
}
} 导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源
我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n
我想要像“嘿那里”这样的东西变成,例如,#316583。我希望将任意长度的字符串“归结”为十六进制颜色。我不知道从哪里开始。我在想,每个字符串的MD5散列都是不同的-但如何将该散列转换为十六进制颜色数字? 最佳答案 你可以只取几位前几位:require'digest/md5'color=Digest::MD5.hexdigest('Mytext')[0..5] 关于ruby-如何使用Ruby基于字母数字字符串生成颜色?,我们在StackOverflow上找到一个类似的问题:
文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3
我正在尝试整个BDD方法并想测试AMQP基于Vanilla的方面Ruby我正在写的应用程序。选择Minitest后作为与其他名副其实的蔬菜框架不同的平衡功能和表现力的测试框架,我着手编写此规范:#File./test/specs/services/my_service_spec.rb#Requirementsfortestrunningandconfigurationrequire"minitest/autorun"require"./test/specs/spec_helper"#Externalrequires#MinitestSpecsforEventMachinerequire
我分1,000个批处理从服务器获取大约20,000个数据集。每个数据集都是一个JSON对象。坚持这会产生大约350MB的未压缩明文。我的内存限制为1GB。因此,我以追加模式将每1,000个JSON对象作为一个数组写入到一个原始JSON文件中。结果是一个包含20个需要聚合的JSON数组的文件。无论如何我都需要触摸它们,因为我想添加元数据。一般RubyYajlParser使这成为可能:raw_file=File.new(path_to_raw_file,'r')json_file=File.new(path_to_json_file,'w')datasets=[]parser=Yajl::
有什么方法可以查看gem是否仅在UNIX/类UNIX系统上受支持?是否有任何gem可以“筛选”所有gem并查看在Windows上使用它是否有任何问题。 最佳答案 简短回答:否。老实说,Windows在Ruby世界里是二等公民。这主要是因为Linux、BSD、OSX和几乎所有其他基于POSIX的系统都同意一件事,而Windows将去做完全不同的事情。即使是用于Windows的gem也可能偶尔会由于开发人员的疏忽而损坏。大多数gem作者没有针对Windows运行并依赖于用户错误报告的持续集成服务器。支持Windows很困难,不仅因为AP