亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

帆船自动舵(自动转向系统)

组件和用品

Arduino UNO
× 1
Arduino Nano R3
× 1

关于这个项目

前言:

我喜欢独自航行,因为当一个人带着他的帆船在海上时,他得到了进化到更高水平所需的一切。在恶劣的天气中在生海中航行可能会非常困难,但如果他选择阳光明媚、风很大的好天气,享受将是最大的。

幸福意味着无限的视野,完美的运动技术,最佳的选择,也意味着人类的东西,如美酒佳肴和美味三明治!正是在这个时候,Autopilot 会派上用场:它代替您工作,同时您下午 5:00 在海上享用茶和饼干。 :-)

Autopilot 能为您做什么:

一艘帆船没有引擎,不能沿着程序化的路径从港口到海滩,再到钓鱼点,绕灯塔转回来,完全不能。

整个工作由Sailor完成,我们必须在这一点上理解它:修剪帆,控制天气和风源/速度,硬化或释放绳索,注意与其他船只的交通,决定方向和转向......当水手决定休息一下,比如说 10 秒或几分钟(著名的“下午茶时间”),他打开自动驾驶仪。在几秒钟内,它的 GPS 获取船只的位置、速度和方向,并能够保持方向(路线)。转向系统是一根与舵相连的操纵杆,通常由专业的水手手移动,现在由 Autopilot 控制,通过滑轮和绳索连接到它的步进电机。

控制舵 是一项持续微调或粗调的工作。船越小(越轻),影响它的方向因素的变化就越大:海浪、风的方向和压力、水手运动引起的船上重量的转移、海流。但是水手总是清醒的,即使是在自动驾驶仪上,通过遥控的方式改变实际路线 :上面有 4 个按钮,标记为 +1 -1 +10 -10,用于度数的大小变化,增加或减少值。这些按钮出现在自动驾驶仪上 还有绿色(右)和红色(左)的。蓝色按钮(中间)用于激活或停用自动驾驶仪,暂停。也是设置参数的黑色按钮 记忆中。

电路:

主要处理由 MCU Arduino Uno 完成 .另一个 MCU,Arduino Nano ,是看门狗:我知道Uno内部确实存在一种看门狗,但我喜欢用独立的外部微控制器来做,这是我一生的梦想,我现在很高兴! Uno 必须通过引脚 3 -> A0 将其置于高/低,5/0 伏,至少每 2.5 秒一次(feedingInterval)来馈送 Nano;如果不是,则表示 Uno 正在“休眠”或“被阻止”,Nano 重置了 Uno... 从来没有发生过,你能相信吗?

它用于流行显示 结合 i2c 电路转换器 两者焊接在一起,最后只用了 4 根线来显着节省数字引脚以与 Uno 通信。也是连接按钮和遥控器的方式 to 由电阻分压器完成,以达到使用尽可能少的 MCU 端口的目标;我选择了 1% 的精度电阻,模拟比较值应该在我输入代码的值之间;如果因为您选择了其他类型的电阻而无法识别某些按钮,也只需对常量进行一些更改(修改“checkRfRC()”和“checkHWButtons()”中的代码)。 RF 433Mhz 遥控 (RC) 电路运行良好;为了提高距离覆盖范围和成功机会,我添加了一个线圈天线,您可以用一根铜线自己制作;我在 10 米外测试了它,但我认为它甚至可以在 20 米或更远的地方工作,考虑到我用来测试 Autopilot 的目标帆船只有 4.20 米长,这已经足够了。

对于 GPS 单元 一开始用的不错的EM406A,可惜发现了Week-Rollover-Bug,太旧了,只好换了一款优秀且受欢迎的北天BN-220T。用它的配置软件请把它设置为每秒“吐”出2次(2Hz)只是必要的“$GNRMC”NMEA串行语句。 GPS 将 (TX) 串行数据发送到 Uno 的引脚 0 (RX)。数据包含用于计算电机校正的所有导航数据:日期、时间、位置纬度和经度、真实航线、速度和卫星定位的有效性。由于 Arduino 的 IDE 编程也使用 pin 0 (RX) 端口,请记住在此操作期间暂时断开 GPS...

我的另一个梦想是使用 EEPROM . IC 2404 是一个漂亮的 512 字节 i2c 集成电路我用来在这个存储芯片中读/写一些步进电机运动的参数我将在“软件”部分解释。

组件列表:

  • Arduino Uno 作为 MCU
  • Arduino Nano 作为看门狗
  • 北天BN-220T GPS
  • 步进电机,型号 23LM,54 步 =1/4 转
  • 电机控制器 Keyes L298
  • RF433Mhz RC XD-YK04 + 4 键遥控器 + 线圈天线
  • 6 个按钮常开(2xRed、2xGreen、1xBlack 和 1xBlue)
  • 电源开关(白色)
  • 用于外部步进电机的母 + 公 6 针圆形连接器
  • 蜂鸣器
  • 显示 LCD1602 2x16 字符 + i2c 转换电路
  • 3 个 LED(红色、蓝色和黄色))
  • IC 24c04 i2c eeprom
  • IC 4051 多路复用器
  • 锂电池 2s 7.4v 2600mA
  • IC 7805 稳压器 + 散热器
  • 热敏电阻 NTC MF52-103 10k
  • 自恢复保险丝 2A
  • 6 个 1N4148 二极管 (D1-D6)
  • 电源屏蔽上的电阻器(R1-R4=10k,R5=100k)
  • 自动驾驶仪屏蔽上的电阻器(R1=330、R2=1k、R3=2k、R4=5.1k、R5=1k、R6/R7/R14=330、R8-R13=10k、R15=10M)
  • 电容器(C1=470uF 16v,C2=100n)
  • 2W 0.22 欧姆电阻 (R6)
  • 公针
  • 女性长针头
  • 外壳透明且“防水”

有几个传感器 在电路上通过 IC 4051 多路复用器 连接到 Arduino Uno .这是一个热敏电阻 控制稳压器散热片温度,一个2W电阻 和 4x10k 作为分压器来计算安培为功耗 整个电路。还有电池电压 受到控制:当单个元素放电低于 3.3v 时,LiPo 被认为是关键的;该电路在一个封装中有两个元件 (2S) LiPo,在低电压(低于 7.0v)的情况下 蜂鸣器 将通过短促的哔哔声通知您。不要等待太久才关机,尽快充电! LED :黄色 以 1Hz 闪烁表示 WatchDog 正在工作; Autopilot 开启时蓝色亮起,暂停时熄灭;按下其中一个遥控器按钮时,红色 LED 闪烁。

所有电路工作电压为 5.0v,由 LiPo 2S 7.4v 2600mA/h 电池和 IC 7805 稳压器提供 .电流不应大于800mA,但通常在100-450mA左右。请给它一个散热片 .热敏电阻放置在上面,温度超过50°C时蜂鸣器会发出哔哔声。

PCB印刷电路板和组装:

使用单面PCB 出于这个原因,我不得不包括一些跳线(虚线的)来解决整个电路的路线。此处显示了组件面,但在下方您有所有文件、组件和焊接面,已镜像,可通过激光打印机在“黄色”或“蓝色”纸张上下载和打印。我用的是黄色的,但他们说蓝色的更好(但价格要高得多)。打印时记得禁用省墨设置,改用 1200 dpi 分辨率以获得深黑色效果。从魔术贴到印刷电路板的墨粉转移过程是通过使用热铁完成的......双面印刷,也印刷在组件面上,可以轻松识别物品的定位,甚至使项目变得“专业”。

两个 PCB 的尺寸都可以将一个作为堆栈放在另一个 Arduino Uno 上 :首先是动力单元,然后是自动驾驶单元。

我的选择是将所有东西放在一起,PCB、MCU、RC、电机驱动电路、电池、GPS、按钮、开关、电线、连接器等。想有一天重复使用它们:我没有将它们焊接在一起,我 使用 标题和 流行的杜邦电线/连接 反而。那么大约有 200 个未焊接的连接,这意味着意外和不需要的故障或电路的不同行为可能会不时发生,这是正常的。 建议是焊接一切 电路更稳定!

参数设置和显示传感器值:

黑色按钮 在盒子的侧面进入设置模式;这也可以在主动导航期间完成,无需先输入暂停。显示屏第一页显示电池电压(V=7.83)、功耗(mA=177)和靠近耗散器的热敏电阻传感器的温度(38°C);一次又一次地按下进入下一页的黑色按钮;第 2、3、4、5 页显示了下面列出的参数,您可以通过 -1 和 +1 按钮更改这些值。第 6 页显示“正在更新...” 如果您更改了某些内容,值将保存在 EEPROM 内存中。

  • 间隔: 即 2000 毫秒,是步进电机将“H”方向恢复到“R”路线,向右或向左移动方向舵的一次尝试和另一次尝试之间的时间;
  • 最低要求: 即 2°,是让 Autopilot 干预的最小偏离航线度数;达到此值时,方向舵会稳定地保持在中心位置;
  • 最大: 即40°,是步进电机一次最大的转向变化;如果计算是 50° 变化,实际上 Stepper 只会移动 40°;
  • 系数: 即 1.50 x °,是一次转向变化的系数;如果计算是 40° 变化,实际上步进电机将移动 (40 x 1.50)=60°;

这些参数是微调自动驾驶仪所必需的 安装在帆船上时。响应度、灵敏度和平滑度取决于滑轮的直径、滑轮的数量、步进电机上主滑轮的直径、舵的灵敏度、舵杆连接在其上的长度等。让我们安装所有东西,然后尝试在船上积累经验。当然,所有测试阶段都选择阳光明媚、微风的日子!

“实时”工作原理:

您正在海上、湖泊或港口附近航行。下午茶时间到了,你的可乐和你最喜欢的三明治在口袋里等着。我们在这里:切换自动驾驶仪 并让它进行卫星 GPS 定位,您现在应该在显示屏上读取以节、时钟和航向为单位的实际速度,即 H270°(R=要遵循的路线,H=实际航向)以度为单位(记住 180°=南,270° =西,360° 或 0°=北,90°=东)。在暂停模式(显示为 STOP)时,R 和 H 值相同。现在连接舵绳,从步进电机到舵杆,然后按蓝色按钮启动自动驾驶仪转向;在这一点上,Autopilot 持有 R=route 方向,它可以控制 H=heading 发生的情况。 标题编号肯定会改变 ,根据我们已经讨论过的天气条件,缓慢或快速。 Autopilot 然后尝试恢复到 R=route 方向 进行修正,即 -10°、+5° 等,直到 H 值等于 R 值 .您可以决定对Route进行一些更改,您可以使用单元上的红色和绿色按钮(-1 -10 +1 +10)或通过遥控器的方式修改编号。 收回控制权 转向 你只需要按下暂停蓝色按钮,从舵杆上断开绳索,用手继续工作。干得好。

软件端:

代码很长,但我希望它足够清晰,易于理解。无论如何,我会解释它是如何做的。草图使用了大约 65% 的程序和大约 45% 的内存。即使使用String类,主要用于Serial NMEA语句操作,整个阐述流程也稳定扎实;它使用“serialEvent()”来每秒两次从 GPS 接收数据 , 然后调用“nmeaExtractData()”,最后用“nmea0183_checksum()”检查数据包以确保数据完整性。如果您使用其他品牌和型号的GPS,请确保句子具有相同的结构,否则您必须在此处进行一些更改. 例如 EM406A 使用 "$GPRMC" 数据包 id , BT220 使用“$GNRMC”代替...只是一个小的名称更改...一个有用的链接可以帮助您进行校验和测试:https://nmeachecksum.eqth.net - 这是一个完整的 NMEA 句子的示例,它包含:id、时间、有效性、纬度、经度、速度、真实航向、日期、变化和校验和。

$ GPRMC, 095836.000, A, 4551.9676, N, 01328.7118, E, 2.09, 341.84, 280519,, *08

在“Setup()”期间检查 EEPROM :如果是新的或未知的,它会被初始化(格式化)。内存中的参数以字节为单位读/写:0=0x29, 1=0x00, 2-3=interval, 4-5=min, 6-7=max, 8-11=coefficient (byte, byte, int, int,漂浮)。我小心地处理了 EEPROM 读写操作,可能是防御过多...... 每 10 秒检查一次传感器 通过多路复用器的“readMuxSensors()”,如果电池电量低或温度高,会引起警报。分辨率功耗低,步进40mA左右。 硬件和遥控按钮 被连续检查;它们的作用取决于“IsSetup”布尔值以及显示“RefreshDisplay()”的作用 .代码的核心是 STEERING CONTROL 部分,它调用“gomotor()”函数来移出和移回步进器;是的,它可能会将方向舵向右移动 10°,并且在间隔值之后它会移回零方向舵位置,以此类推在新一轮计算之后。如前所述,转向工作也在设置期间进行,因为它只影响几个按钮和显示行为。 Whatchdog 喂食 非常简单但很重要:只需尽快打开/关闭其 Pin 图。

如何将其安装在帆船上:

如下图所示,我选择将 Autopilot 和 Stepper Motor 放置在船尾,两者都用螺栓等固定好;一根直径为 6mm 的绳索从主电机滑轮开始,绕着放置在两侧的另外两个滑轮。这两个滑轮应该通过两个蹦极环的方式“固定”在船上,以保持绳索的轻微张紧。此时,最后要决定如何将绳子连接到舵杆上(临时连接);它必须在您希望 Autopilot 运行时连接,易于连接和断开连接。保持自动驾驶系统远离水! :-)

新闻和更新:

  • 2020 年 5 月 10 日,添加用于下载步进皮带轮(由我提供)和安装板(由 Andrew Barney 提供)的 .STEP 3D CAD 项目文件,以及它们的 3D 预览图片。

免责声明和警告:

假设这是我们在这里玩的游戏,没什么 认真对待!几年前,我乘帆船环游世界进行了 16 个月的长途旅行。我们使用真正的自动驾驶仪(不是 这个 一个!) 在所有天气条件下,即使是恶劣的天气条件。真正的自动驾驶仪很重要 非常 您必须信任的硬件和软件 很多。代替这个 Arduino Autopilot 是一款非常棒的游戏 与和 花时间玩乐。

玩得开心!

马可·宗卡

代码

  • 自动驾驶草图(用于 Uno)
  • WatchDog 草图(用于 Nano)
Autopilot 草图(用于 Uno)Arduino
/* 此草图充当小型帆船的自动驾驶仪,作者 Marco Zonca,2019 年 Arduino UNO 作为 CPU,Arduino Nano 作为看门狗,GPS BT-220 nmea,步进电机 + 控制器,rf433Mhz RC,6 个按钮,蜂鸣器,i2c显示器,2 个 LED,i2c 24c04 eeprom,传感器的 Mux 4051,lipo 2s 7.4v 2600mA,7805 稳压器,热敏电阻;*/#include #include #include String inputString ="";String nm_time ="00:00:00";String nm_validity ="V";String nm_latitude ="ddmm.mmmm'N";String nm_longitude ="dddmm.mmmm'E ";String nm_knots ="0.0kn";float nmf_knots =0.0;String nm_truecourse ="360";float nmf_truecourse =360;String nm_date ="dd/mm/yyyy";String nm_routetofollow ="000";float nmf_routetofollow =0 unsigned long previousStearingMillis =0;unsigned long currentStearingMillis =0;unsigned long prevCheckSensorsMillis =0;unsigned long currCheckSensorsMillis =0;int CheckSensorsInterval =10000;bool stringComplete =false;bool isfirstfix =true;bool ispause =true;bool isStearing =false;bool isSetup =false;int s=0;int y=0;int z=0;int d=0;int rfRemoteControlValue =0;int HWButtonValue =0;int SetupParameter =0;float calcmove =0;float cm =0;float Stearing =0;float prevStearing =0;float t =0;int EEdisk =0x50;int EEid1 =0x29;int EEid2 =0x00;unsigned int EEaddress =0;unsigned int EEbytes =12;byte EEdata[12];byte EEbytedata;int EEerr =0;float SensorVBatt=0;float SensorVRes=0;float SensorTemp=0;float SensormAmp=0;//以下参数为默认值,但可读写eeprom// 如果地址 0 和 1 的内容不同,则 eeprom 被初始化 addres len type notes// 0x50 EEdisk 处为 0-255 字节,0x51 处为 256-512 字节(未使用) ---------- -------------------------------------------------- ---// 0 1B byte 01001001 (0x29 as autopilot project id1)// 1 1B byte 00000000 (0x00 " " id2)int StearingInterval =2000; // 尝试和返回之间的毫秒数 2 2B int StearingInterval 1000-5000 步 100int StearingMinToMove =2; // compass_degrees 4 2B int StearingMinToMove 0-20 步 1int StearingMaxMove =40; // compass_degrees 6 2B int StearingMaxMove 10-45 步 1float StearingCoeffMove =1.5; // 用作 (compass_degrees * coeff) 8 4B float StearingCoeffMove 0.1-4 steps 0.1// 12 free//byte bStearingInterval[sizeof(int)];byte bStearingMinToMove[sizeof(int)];byte bStearingMaxMove[sizeof(int)];byte bStearingCoeffMove[sizeof(float)];int prev_StearingInterval=0;int prev_StearingMinToMove=0;int prev_StearingMaxMove=0;float prev_StearingCoeffMove=0;const int ledpausePin =2;const int watchDogPin int =xSconPin =3; // 00=Vin 01=Vbatt 10=Tempconst int MuxSelBit1Pin =6; // const int motorABenablePin =13;const int MuxIOPin =14;const int ButtonsPin =15;const int rfRemoteControlPin =16;const int SpeakerPin =17;const int RCleftbutton =201;const int RCrightbutton =202;const =202;const;const const int RCright10button =204;const int HWleftbutton =101;const int HWrightbutton =102;const int HWpausebutton =103;const int HWsetupbutton =104;const int HWleft10button =105;const int HWright10Button =106 // 200 for model 23LM, 54 steps =1/4 of rotationLiquidCrystal_I2C lcd (0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);Stepper motor(motorStepsPerRevolution, 9, 10, 11, 12 );void setup() { Serial.begin(4800);液晶显示器。开始(16,2); Wire.begin(); motor.setSpeed(60); inputString.reserve(200); pinMode(motorsABenablePin,输出); pinMode(MuxSelBit0Pin,输出); pinMode(MuxSelBit1Pin,输出);数字写入(电机ABenablePin,低);数字写入(MuxSelBit0Pin,低);数字写入(MuxSelBit1Pin,低); pinMode(ledpausepin,输出); pinMode(看门狗Pin,输出);数字写入(ledpausePin,低);数字写入(看门狗针,低); // 读取+检查 EEPROM(格式化是否为新的(或未识别)) lcd.clear(); lcd.setCursor(0,0); lcd.print("内存检查..."); lcd.setCursor(0,1); for (s =0; s =CheckSensorsInterval) { readMuxSensors(); if ((SensorVBatt <=7.0) || (SensorTemp>
=50)) { lcd.clear(); lcd.setCursor(0,0); lcd.print("报警传感器!"); lcd.setCursor(1,1); lcd.print("V="); lcd.print(SensorVBatt);液晶打印(“”); lcd.print(int(SensorTemp));液晶显示器(0xDF);液晶打印(“C”); NewTone (speakerPin,10);延迟(1000); noNewTone(); } prevCheckSensorsMillis =currCheckSensorsMillis; } // STEARING CONTROL ---------------- currentStearingMillis =millis(); if (currentStearingMillis - previousStearingMillis>=StearingInterval) { if (isStearing ==false &&ispause ==false) { // 去试试 (move stearing) calcmove =nmf_routetofollow - nmf_truecourse; if (calcmove <(-180)) { calcmove =calcmove + 360; } else { if (calcmove> (+180)) { calcmove =calcmove - 360; } } if (abs(calcmove)>=StearingMinToMove) { if (abs(calcmove)>=StearingMaxMove) { if (calcmove <0) { cm =(StearingMaxMove * -1);计算移动 =厘米; } else { cm =(StearingMaxMove * 1);计算移动 =厘米;斯蒂林 =(calcmove * StearingCoeffMove); gomotor(int((Stearing * 216) / 360)); // 54 步 =1/4 转 prevStearing =Stearing; isStearing =true; } } else { // 返回(将转向移动到“零”位置) if (isStearing ==true) { Stearing =(prevStearing * -1); gomotor(int((Stearing * 216) / 360)); // 54 步 =1/4 转 Stearing =0;上一个斯特林 =0; isStearing =false; } } previousStearingMillis =currentStearingMillis; } // RC RF 按钮 ------------------ rfRemoteControlValue =checkRfRC(); if (rfRemoteControlValue) { switch (rfRemoteControlValue) { case RCleftbutton:// 左遥控按钮 goleft();休息; case RCrightbutton:// 右 RC 按钮 goright();休息; case RCleft10button:// Left-10 RC 按钮 goleft10();休息; case RCright10button:// Right+10 RC 按钮 goright10();休息; } } // 按钮 ------------------------ HWButtonValue =checkHWButtons(); if (HWButtonValue) { switch (HWButtonValue) { case HWleftbutton:// Left(-1) HW button if (isSetup ==false) { goleft(); } else { setupMinus(); } 休息; case HWrightbutton:// Right(+1) HW button if (isSetup ==false) { goright(); } else { setupPlus(); } 休息; case HWpausebutton:// 暂停硬件按钮 gopause();休息; case HWsetupbutton:// 设置硬件按钮 gosetup();休息; case HWleft10button:// Left(-10) HW button goleft10();休息; case HWright10button:// Right(+10) HW button goright10();休息; } } // GPS NMEA ------------------ if (stringComplete ==true) { // 通过串口接收到 nmea 语句 RX bool ret; ret =nmeaExtractData(); inputString =""; stringComplete =假; if (ret ==true) { RefreshDisplay(); } } // 看门狗喂食---------------- if (digitalRead(watchDogPin) ==LOW) { digitalWrite(watchDogPin, HIGH); } else { digitalWrite(watchDogPin, LOW); }}// 在多路复用器上读取传感器void readMuxSensors() { float Vo =0;浮动 n =0;浮动 n1 =0;浮动 v1ad =0;浮动 v2ad =0;浮动校正 =0;浮动 R1 =10000;浮动 logR2 =0;浮点 R2 =0;浮动 T =0;浮动 c1 =1.009249522e-03;浮动 c2 =2.378405444e-04;浮动 c3 =2.019202697e-07;数字写入(MuxSelBit0Pin,低); // 00=Vbatt digitalWrite(MuxSelBit1Pin, LOW); n =模拟读取(MuxIOPin); v1ad=n; n1=(((10.00 * n) / 1023.00)); SensorVBatt=(n1 + ((n1 * 0.0) /100)); // 任意校正(未激活 =0.0%) digitalWrite(MuxSelBit0Pin, LOW); // 01=Vres digitalWrite(MuxSelBit1Pin, HIGH); n =模拟读取(MuxIOPin); v2ad=n; n1=(((10.00 * n) / 1023.00)); SensorVRes=(n1 + ((n1 * 0.0) /100)); // 任意校正(未激活 =0.0%) digitalWrite(MuxSelBit0Pin, HIGH); // 10=NTC Temp digitalWrite(MuxSelBit1Pin, LOW); Vo =模拟读取(MuxIOPin); R2 =R1 * (1023.0 / Vo - 1.0); logR2 =log(R2); T =(1.0 / (c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2));传感器温度 =T - 273.15; // 摄氏 n =(v1ad - v2ad); n1 =(n / 0.22) * 1000.00; SensormAmp =(((10.00 * n1) / 1023.00));}// 从nmea中提取数据 inputStringbool nmeaExtractData() { bool ret =false; //true if nmea sentence =$GNRMC 和有效的CHKSUM if ((inputString.substring(0,6) =="$GNRMC") &&(inputString.substring(inputString.length()-4,inputString.length()- 2) ==nmea0183_checksum(inputString))) { y=0; for (s =1; s <11; s ++) { y=inputString.indexOf(",",y); switch (s) { case 1://time z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_time=inputString.substring(y+1,y+2+1)+":"+inputString.substring(y+1+2,y+4+1)+" :"+inputString.substring(y+1+4,y+6+1); y=z;休息; case 2://有效性 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_validity=inputString.substring(y+1,y+1+1); y=z;休息; case 3://纬度z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_latitude=inputString.substring(y+1,y+2+1)+""+inputString.substring(y+1+2,y+10+1)+"' ”; y=z;休息; case 4://北/南 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_latitude=nm_latitude + inputString.substring(y+1,y+1+1); y=z;休息; case 5://longitude z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_longitude=inputString.substring(y+1,y+3+1)+""+inputString.substring(y+1+3,y+11+1)+"' ”; y=z;休息; case 6://东/西 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_longitude=nm_longitude + inputString.substring(y+1,y+1+1); y=z;休息; case 7://速度节 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nmf_knots=inputString.substring(y+1,z).toFloat(); t=roundOneDec(nmf_knots); nm_knots=String(t,1)+"kn"; y=z;休息; case 8://true course z=inputString.indexOf(",",y+1); if (z>(y+1)) { nmf_truecourse=inputString.substring(y+1,z).toFloat(); d=nmf_truecourse; nm_truecourse=d; y=z;休息; case 9://日期 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_date=inputString.substring(y+1,y+2+1)+"/"+inputString.substring(y+1+2,y+4+1)+" /20"+inputString.substring(y+1+4,y+6+1); y=z;休息; case 10:// 语句中断;默认值:// 语句中断; } } if ((isfirstfix ==true) || (ispause ==true)) { nm_routetofollow=nm_truecourse; nmf_routetofollow=nmf_truecourse; isfirstfix=false; } ret=true; } return ret;}// setupvoid setupPlus() { switch (SetupParameter) { case 2://interval StearingInterval =(StearingInterval + 100);如果(StearingInterval> 5000){ StearingInterval =5000; } 休息;情况 3://分钟。移动 StearingMinToMove =(StearingMinToMove + 1);如果 (StearingMinToMove> 20) { StearingMinToMove =20; } 休息;情况 4://最大。移动 StearingMaxMove =(StearingMaxMove + 1);如果(StearingMaxMove> 45){ StearingMaxMove =45; } 休息; case 5://系数StearingCoeffMove =(StearingCoeffMove + 0.1);如果(StearingCoeffMove> 4){ StearingCoeffMove =4; } 休息; } 延迟(200); RefreshDisplay();}// setupvoid setupMinus() { switch (SetupParameter) { case 2://interval StearingInterval =(StearingInterval - 100);如果(StearingInterval <1000){ StearingInterval =1000; } 休息;情况 3://分钟。移动 StearingMinToMove =(StearingMinToMove - 1);如果(StearingMinToMove <0){ StearingMinToMove =0; } 休息;情况 4://最大。移动 StearingMaxMove =(StearingMaxMove - 1); if (StearingMaxMove <10) { StearingMaxMove =10; } 休息; case 5://系数StearingCoeffMove =(StearingCoeffMove - 0.1);如果(StearingCoeffMove <0.1){ StearingCoeffMove =0.1; } 休息; } 延迟(200); RefreshDisplay();}// 电机控制 (+)=forward (-)=backwardsvoid gomotor(int stepsToMove) { digitalWrite(motorsABenablePin, HIGH); motor.step(stepsToMove); digitalWrite(motorsABenablePin, LOW);}// 刷新显示数据void RefreshDisplay() { if (isSetup ==false) { //---------normal lcd.clear(); lcd.setCursor(0,0); lcd.print("R"+nm_routetofollow);液晶显示器(0xDF); lcd.print("H"+nm_truecourse);液晶显示器(0xDF);如果(ispause ==true){ lcd.print(“停止”); } else { if (Stearing> 0) { lcd.print(" +"); } if (Stearing ==0) { lcd.print(" "); } if (Stearing <0) { lcd.print(" "); } lcd.print(int(Stearing)); } lcd.setCursor(0,1); lcd.print(nm_time+""+nm_knots); } if (isSetup ==true) { //-----------setup lcd.clear(); lcd.setCursor(0,0); lcd.print("设置:"); switch (SetupParameter) { case 1://显示传感器 readMuxSensors(); lcd.print("V="); lcd.print(SensorVBatt); lcd.setCursor(1,1); lcd.print("mA="); lcd.print(int(SensormAmp));液晶打印(“”); lcd.print(int(SensorTemp));液晶显示器(0xDF);液晶打印(“C”);休息; case 2://interval lcd.print("interval"); lcd.setCursor(7,1); lcd.print(StearingInterval); lcd.print(" mSec");休息; case 3://min. to move lcd.print("minimum"); lcd.setCursor(7,1); lcd.print(StearingMinToMove); lcd.write(0xDF);休息; case 4://max. move lcd.print("max"); lcd.setCursor(7,1); lcd.print(StearingMaxMove); lcd.write(0xDF);休息; case 5://coefficient lcd.print("coeffic."); lcd.setCursor(7,1); lcd.print(StearingCoeffMove); lcd.print(" x "); lcd.write(0xDF);休息; } }}/* 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(); inputString +=inChar; // if the incoming character is a newline, set a flag so the main loop can // do something about it if (inChar =='\n') { stringComplete =true; } } }//calculate checksum of nmea sentenceString nmea0183_checksum(String nmea_data) { int crc =0; String chSumString ="";国际我; // ignore the first $ sign, checksum in sentence for (i =1; i <(nmea_data.length()-5); i ++) { // remove the - 5 if no "*" + cksum + cr + lf are present crc ^=nmea_data[i]; } chSumString =String(crc,HEX); if (chSumString.length()==1) { chSumString="0"+chSumString.substring(0,1); } chSumString.toUpperCase(); return chSumString;}//check RC which button is pressedint checkRfRC() { int n =0; int res =0; n =analogRead(rfRemoteControlPin); if ((n>350) and (n<460)) { // button A res =RCleftbutton; } if ((n> 90) and (n<190)) { // button B res =RCrightbutton; } if ((n>540) and (n<640)) { // button C res =RCleft10button; } if ((n>225) and (n<325)) { // button D res =RCright10button; } return res; }//check HW which button is pressedint checkHWButtons() { int n =0; int res =0; n =analogRead(ButtonsPin); //Serial.println(n); if ((n>465) and (n<565)) { // button left res =HWleftbutton; } if ((n>290) and (n<390)) { // button right res =HWrightbutton; } if ((n>130) and (n<220)) { // button pause res =HWpausebutton; } if ((n>625) and (n<725)) { // button setup res =HWsetupbutton; } if ((n>975) and (n<1075)) { // button left-10 res =HWleft10button; } if ((n>800) and (n<900)) { // button right+10 res =HWright10button; } return res; }void gosetup() { // setup button if (isSetup ==false) { SetupParameter =1; isSetup =true; } else { if (SetupParameter <5) { SetupParameter ++; } else { if (prev_StearingInterval !=StearingInterval || prev_StearingMinToMove !=StearingMinToMove || prev_StearingMaxMove !=StearingMaxMove || prev_StearingCoeffMove !=StearingCoeffMove) { lcd.clear(); lcd.setCursor(0,0); lcd.print("updating... ");延迟(1000); goupdateEEPROM(); if (EEerr) { lcd.print("E="); lcd.print(EEerr);延迟(1000); } prev_StearingInterval =StearingInterval; prev_StearingMinToMove =StearingMinToMove; prev_StearingMaxMove =StearingMaxMove; prev_StearingCoeffMove =StearingCoeffMove; } isSetup =false; } } NewTone (speakerPin,2000);延迟(200); noNewTone(); RefreshDisplay();}void goupdateEEPROM() { EEaddress =0; //id1 EEdata[0] =EEid1; EEbytedata =EEid1; writeEEPROM (EEdisk, EEaddress, EEbytedata); EEaddress =1; //id2 EEdata[1] =EEid2; EEbytedata =EEid2; writeEEPROM (EEdisk, EEaddress, EEbytedata); memcpy(bStearingInterval, &StearingInterval, sizeof(int)); memcpy(bStearingMinToMove, &StearingMinToMove, sizeof(int)); memcpy(bStearingMaxMove, &StearingMaxMove, sizeof(int)); memcpy(bStearingCoeffMove, &StearingCoeffMove, sizeof(float)); memcpy(EEdata+2,bStearingInterval,sizeof(int)); memcpy(EEdata+4,bStearingMinToMove,sizeof(int)); memcpy(EEdata+6,bStearingMaxMove,sizeof(int)); memcpy(EEdata+8,bStearingCoeffMove,sizeof(float)); for (s =2; s  360) { nmf_routetofollow =1; } d=nmf_routetofollow; nmf_routetofollow=d; nm_routetofollow=d; NewTone (speakerPin,800);延迟(200); noNewTone(); } else { NewTone (speakerPin,1000);延迟(50); noNewTone(); } RefreshDisplay();}void goright10() { // right 10x button/RC if (ispause ==false) { for (s =1; s <11; s ++) { nmf_routetofollow ++; if (nmf_routetofollow> 360) { nmf_routetofollow =1; } } d=nmf_routetofollow; nmf_routetofollow=d; nm_routetofollow=d; NewTone (speakerPin,800);延迟(200); noNewTone(); } else { NewTone (speakerPin,1000);延迟(50); noNewTone(); } RefreshDisplay();}void gopause() { // pause button/RC if (ispause ==true) { ispause=false; digitalWrite(ledpausePin, HIGH); NewTone (speakerPin,50);延迟(200); NewTone (speakerPin,200); delay(800); noNewTone(); } else { ispause=true; digitalWrite(ledpausePin, LOW); NewTone (speakerPin,200);延迟(200); NewTone (speakerPin,50); delay(800); noNewTone(); } RefreshDisplay();}// reading eeprombyte readEEPROM (int diskaddress, unsigned int memaddress) { byte rdata =0x00; Wire.beginTransmission (diskaddress); Wire.write (memaddress); if (Wire.endTransmission () ==0) { Wire.requestFrom (diskaddress,1); if (Wire.available()) { rdata =Wire.read(); } else { EEerr =1; //"READ no data available" } } else { EEerr =2; //"READ eTX error" } Wire.endTransmission (true); return rdata;}// writing eepromvoid writeEEPROM (int diskaddress, unsigned int memaddress, byte bytedata) { Wire.beginTransmission (diskaddress); Wire.write (memaddress); Wire.write (bytedata); if (Wire.endTransmission () !=0) { EEerr =3; //"WRITING eTX error" } Wire.endTransmission (true);延迟(5); }// round zero decimalfloat roundZeroDec(float f) { float y, d; y =f*1; d =y - (int)y; y =(float)(int)(f*1)/1; if (d>=0.5) { y +=1; } else { if (d <-0.5) { y -=1; } } return y;}// round one decimalfloat roundOneDec(float f) { float y, d; y =f*10; d =y - (int)y; y =(float)(int)(f*10)/10; if (d>=0.5) { y +=0.1; } else { if (d <-0.5) { y -=0.1; } } return y;}// round two decimalfloat roundTwoDec(float f) { float y, d; y =f*100; d =y - (int)y; y =(float)(int)(f*100)/100; if (d>=0.5) { y +=0.01; } else { if (d <-0.5) { y -=0.01; } } return y;}
WatchDog sketch (for Nano)Arduino
/* * This sketch is a Watchdog to keep CLIENT under control, on Arduino NANO 3.0 by Marco Zonca * CLIENT must feed Whatcdog sooner then feedingInterval otherwise will be forced to restart * */const int feedingPin =14;const int ledPin =15;const int restartPin =16;const int buzzerPin =17;const long ledInterval =1000;const long feedingInterval =2500;const long timeForClientStart =16000;int ledState =LOW;int previousFeedingState =LOW;int feedingState =LOW;unsigned long previousLedMillis =0;unsigned long previousFeedingMillis =0;void setup() { digitalWrite(restartPin, HIGH); // LOW will force CLIENT to restart pinMode(ledPin, OUTPUT); pinMode(buzzerPin, OUTPUT); pinMode(restartPin, OUTPUT); pinMode(feedingPin, INPUT); delay(timeForClientStart); // let time to CLIENT to start...}void loop() { unsigned long currentMillis =millis(); // BLINK LED ------------------- if (currentMillis - previousLedMillis>=ledInterval) { previousLedMillis =currentMillis; if (ledState ==LOW) { ledState =HIGH; } else { ledState =LOW; } digitalWrite(ledPin, ledState); } // CHECK THE FEEDING ------------------- feedingState =digitalRead(feedingPin); // CLIENT must set pin HIGH -> LOW frequently to prove it's alive if (feedingState ==HIGH) { if (previousFeedingState ==LOW) { previousFeedingMillis =currentMillis; } previousFeedingState =HIGH; } else { previousFeedingState =LOW; } if (currentMillis - previousFeedingMillis> feedingInterval) { // CLIENT is sleeping ledState =HIGH; digitalWrite(ledPin, ledState); tone(buzzerPin,1500);延迟(500); digitalWrite(restartPin, LOW); //restart CLIENT tone(buzzerPin,1500);延迟(500); digitalWrite(restartPin, HIGH); tone(buzzerPin,1500); delay(timeForClientStart); // let CLIENT time to restart... noTone(buzzerPin); currentMillis =millis(); previousFeedingState =LOW; previousFeedingMillis =currentMillis; previousLedMillis =currentMillis; }}

定制零件和外壳

23lm-stepper-plate-v2_PlvJaff9Hl.step 23lm-stepper-pulley-56_UhsbaWbiBt.step

示意图


制造工艺

  1. 用于 Raspberry Pi 的 DIY 红外运动传感器系统
  2. 为您的无人机构建弹道降落伞回收系统
  3. 持续监控系统适合您吗?
  4. 机器人的自动工具更换器
  5. 您的系统准备好迎接物联网了吗?
  6. 自动车床用多边形刀具
  7. 精密零件CNC自动车床
  8. 高超音速飞行推进系统
  9. 手动和自动变速器系统的工作原理
  10. 了解自动变速器系统
  11. 了解自动润滑系统
  12. 为最佳性能建立系统基线