<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>单片机 &#8211; Daoker小站</title>
	<atom:link href="https://daoker.cc/category/%E5%8D%95%E7%89%87%E6%9C%BA/feed" rel="self" type="application/rss+xml" />
	<link>https://daoker.cc</link>
	<description>个人随笔小记</description>
	<lastBuildDate>Thu, 02 Mar 2023 07:42:12 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.1</generator>

<image>
	<url>https://daoker.cc/wp-content/uploads/2022/08/cropped-daoker_blog_s-32x32.png</url>
	<title>单片机 &#8211; Daoker小站</title>
	<link>https://daoker.cc</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>基于二维激光雷达的蔗田垄高检测机器人——现场试验</title>
		<link>https://daoker.cc/daokerto695.html</link>
					<comments>https://daoker.cc/daokerto695.html#respond</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Wed, 23 Nov 2022 01:56:09 +0000</pubDate>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[ROS]]></category>
		<category><![CDATA[单片机]]></category>
		<category><![CDATA[机器人]]></category>
		<category><![CDATA[机器视觉]]></category>
		<category><![CDATA[树莓派]]></category>
		<guid isPermaLink="false">https://daoker.cc/?p=695</guid>

					<description><![CDATA[遗忘越来越快 之前做仿真时学看ros，然后休息了几个月，现在要做试验了，感觉已经忘得差不多了，所以体会到了做笔 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>遗忘越来越快</p>
<cite>之前做仿真时学看ros，然后休息了几个月，现在要做试验了，感觉已经忘得差不多了，所以体会到了做笔记的重要性，因此这里做个试验笔记。</cite></blockquote>



<p>参考资料:<a rel="noreferrer noopener" href="http://www.autolabor.com.cn/book/ROSTutorials/" target="_blank">http://www.autolabor.com.cn/book/ROSTutorials/</a></p>



<p>对于试验，本质就是搭建一个机器人，从控制的角度看，机器人系统可以分为：传感系统、控制系统、驱动系统、执行机构。</p>



<p><strong>执行机构:</strong>&nbsp;执行机构只要满足能在蔗田环境下工作，采用四个直流编码电机带动四个主动轮进行行走，由于执行机构比较简单，不再做单独介绍。</p>



<p><strong>驱动系统:&nbsp;</strong>电池、arduino 以及电机驱动模块；</p>



<p><strong>控制系统:</strong>&nbsp;树莓派；</p>



<p><strong>传感系统:</strong>&nbsp;编码器、单线激光雷达、IMU；</p>



<p>其中，执行机构与驱动系统构成了机器人底盘。</p>



<h2 class="wp-block-heading">执行机构和驱动系统</h2>



<h3 class="wp-block-heading">执行结构</h3>



<h4 class="wp-block-heading">硬件介绍</h4>



<p><s>底盘是淘宝买的四驱mini小车，由于上位机是树莓派下的ROS，因此为了省事，把重点放在算法的研究上，驱动控制板选择了另一家和ros深度配合的驱动板，该驱动板基于stm32，并且集成了imu惯性传感器，所以把原来买的IMU退了。</s></p>



<p>经过仔细阅读说明书，发现小车底盘和深度定制的ros驱动板不太兼容，目前有两种方案：</p>



<p>1.驱动板和树莓派ros深度定制，例程丰富，开发手册详细，但是其配的小车不太适合我的的试验环境</p>



<p>2.例程很粗糙，开发手册没有，对应的小车比较适合我们的试验环境</p>



<p>最终我选择了第二种，因为首先试验要保证的是硬件条件能满足试验环境，对于软件环境，我觉得可以多看多学，慢慢研究，并且我也不是完全没基础，对于1中的教程有的显得太过于多余。对于方案2，要考虑的是使用arduino进行四驱小车的pid控制，对于imu信息的接收，可以由arduino承担，也可以直接由树莓派承担，最终的目的就是树莓派中的ros里有imu数据的话题，有编码器的话题，同时考虑将他们如何做成odom信息。</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-24.png" alt="" class="wp-image-708" width="296" height="459" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-24.png 357w, https://daoker.cc/wp-content/uploads/2022/11/image-24-193x300.png 193w" sizes="(max-width: 296px) 100vw, 296px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img decoding="async" width="380" height="702" src="http://daoker.cc/wp-content/uploads/2022/11/image-25.png" alt="" class="wp-image-709" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-25.png 380w, https://daoker.cc/wp-content/uploads/2022/11/image-25-162x300.png 162w" sizes="(max-width: 380px) 100vw, 380px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-26.png" alt="" class="wp-image-710" width="407" height="453" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-26.png 706w, https://daoker.cc/wp-content/uploads/2022/11/image-26-269x300.png 269w" sizes="(max-width: 407px) 100vw, 407px" /></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="690" height="672" src="http://daoker.cc/wp-content/uploads/2022/11/image-33.png" alt="" class="wp-image-741" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-33.png 690w, https://daoker.cc/wp-content/uploads/2022/11/image-33-300x292.png 300w" sizes="auto, (max-width: 690px) 100vw, 690px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/701b4f72a6d79aa2cfb7ec947c6b6c2.png" alt="" class="wp-image-742" width="308" height="248" srcset="https://daoker.cc/wp-content/uploads/2022/11/701b4f72a6d79aa2cfb7ec947c6b6c2.png 920w, https://daoker.cc/wp-content/uploads/2022/11/701b4f72a6d79aa2cfb7ec947c6b6c2-300x242.png 300w, https://daoker.cc/wp-content/uploads/2022/11/701b4f72a6d79aa2cfb7ec947c6b6c2-768x619.png 768w" sizes="auto, (max-width: 308px) 100vw, 308px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"></div>
</div>



<div class="wp-block-argon-collapse collapse-block shadow-sm collapsed hide-border-left" style="border-left-color:#ffffff00"><div class="collapse-block-title" style="background-color:#ffffff00"><span class="collapse-block-title-inner">弃用的方案</span><i class="collapse-icon fa fa-angle-down"></i></div><div class="collapse-block-body" style="display: none">这里要编写STM32的底层源码<br>ΜVISION5安装包和安装破解：HTTPS://WWW.MR-WU.CN/KEIL-MDK-UVISION-5-CRACK<img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-30.png" alt="" style="width: 500px;">STM32控制器程序执行图<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-31-1024x463.png" alt="" style="width: 500px;">初始化和任务对应的代码</div></div>



<p></p>



<h4 class="wp-block-heading">接线</h4>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="458" src="http://daoker.cc/wp-content/uploads/2022/11/image-34-1024x458.png" alt="" class="wp-image-743" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-34-1024x458.png 1024w, https://daoker.cc/wp-content/uploads/2022/11/image-34-300x134.png 300w, https://daoker.cc/wp-content/uploads/2022/11/image-34-768x343.png 768w, https://daoker.cc/wp-content/uploads/2022/11/image-34-1536x687.png 1536w, https://daoker.cc/wp-content/uploads/2022/11/image-34.png 1539w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">驱动系统</h3>



<div class="wp-block-argon-collapse collapse-block shadow-sm collapsed hide-border-left" style="border-left-color:#ffffff00"><div class="collapse-block-title" style="background-color:#ffffff00"><span class="collapse-block-title-inner"><a href="https://www.cnblogs.com/1996jiwei/p/6561681.html">Arduino Wire.h（IIC）库函数详解</a></span><i class="collapse-icon fa fa-angle-down"></i></div><div class="collapse-block-body" style="display: none">此库中包含<br>1 Wire.begin()<br>2 Wire.requestFrom()<br>3 Wire.beginTransmission()<br>4 Wire.endTransmission()<br>5 Wire.write()<br>6 Wire.available()<br>7 Wire.read()<br>8 Wire.onReceive()<br>9 Wire.onRequest()<br>Wire.begin() 和 Wire.begin(address)<br>描述<br>初始化wire库 ，并且加入到I2C网络，前者作为Master 或 Slaver，并且只能调用一次<br>参数<br>address ：7位的器件地址（可选），如果没有输入，则以Master的形式加入到I2C网络<br>Wire.requestFrom()<br>描述<br>主设备请求从设备一个字节，这个字节可以被主设备用 read()或available()接受<br>Wire.requrstFrom(addtess,quantity)<br>Wire.requrstFrom(addtess,quantity,stop)<br>参数<br>addtess ： 7位的器件地址<br>quantity ： 请求得到的数量<br>stop ：布尔形，‘1’ 则在请求结束后发送一个停止命令，并释放总线。‘0’则继续发送请求保持连接<br>返回<br>字节形 ，从从机接受到的字节数目<br>Wire.beginTransmission(address)<br>描述<br>开始一次传输数据，发送一个I2C开始字符<br>参数<br>address ： 器件的7位地址<br>返回<br>none<br>Wire.endTransmission()和Wire.endTransmission(stop)<br>描述<br>结束一个由beginTransmission（）开始的并且由write（）排列的从机的传输。在Arduino中 endTransmission（）接受到<br>一个布尔形变量，如果为1 则endTransmission（）发送一个停止信息；如果为0 则发送开始信息<br>返回<br>0 成功 1 数据溢出 2 发送addtess时从机接受到NACK 3 发送数据时接受到 NACK 4 其他错误<br>Wire.write()<br>描述<br>向从机发送数据<br>形式<br>Wire.write(value)<br>Wire.write(string)<br>Wire.write(data, length)<br>参数<br>value ：要发送的数值<br>string ：字符组的指针<br>data ：一个字节数组<br>length ：传输的数量<br>Wire.available()<br>描述<br>Wire.requestFrom()请求从机数据后，可以使用available接收<br>Wire.read()<br>描述<br>Wire.requestFrom()请求从机数据后，可以使用read接收<br>Wire.onReceive()<br>描述<br>从机接收主机发来的数据<br>Wire.onRequest()<br>描述<br>从机请求主机发送数据</div></div>



<p>对于I2C,其实还有个更新的库，但是这里驱动板的示例程序是基于上面的，为了节约时间，新库以后有时间再研究，新库：</p>



<div class="wp-block-argon-github github-info-card card shadow-sm github-info-card-full" data-author="rambo" data-project="I2C"><div class="github-info-card-header"><a href="https://github.com/" target="_blank" title="Github" rel="noopener"><span><i class="fa fa-github"></i> GitHub</span></a></div><div class="github-info-card-body"><div class="github-info-card-name-a"><a href="https://github.com/rambo/I2C" target="_blank" rel="noopener"><span class="github-info-card-name">rambo/I2C</span></a></div><div class="github-info-card-description"></div></div><div class="github-info-card-bottom"><span class="github-info-card-meta github-info-card-meta-stars"><i class="fa fa-star"></i> <span class="github-info-card-stars"></span></span><span class="github-info-card-meta github-info-card-meta-forks"><i class="fa fa-code-fork"></i> <span class="github-info-card-forks"></span></span></div></div>



<h4 class="wp-block-heading">驱动板使用代码学习：</h4>



<div  class='collapse-block shadow-sm collapse-block-transparent collapsed hide-border-left'><div class='collapse-block-title'><i class='fa fa-flag'></i> <span class='collapse-block-title-inner'>内容过长，点击展开</span><i class='collapse-icon fa fa-angle-down'></i></div><div class='collapse-block-body' style='display:none;'></p>



<pre class="wp-block-code"><code>
#include &lt;Wire.h&gt;

#define I2C_ADDR        0x34            //12C地址


#define ADC_BAT_ADDR                  0   //电压地址
#define MOTOR_TYPE_ADDR               20 //编码电机类型设置
#define MOTOR_ENCODER_POLARITY_ADDR   21 //设置编码方向极性，
//如果发现电机转速根本不受控制，要么最快速度转动，要么停止。可以将此地址的值重新设置一下
//范围0或1，默认0
#define MOTOR_FIXED_PWM_ADDR      31 //固定PWM控制，属于开环控制,范围（-100~100）
//#define SERVOS_ADDR_CMD 40        
#define MOTOR_FIXED_SPEED_ADDR    51 //固定转速控制，属于闭环控制，
//单位：脉冲数每10毫秒，范围（根据具体的编码电机来，受编码线数，电压大小，负载大小等影响，一般在±50左右）

#define MOTOR_ENCODER_TOTAL_ADDR  60 //4个编码电机各自的总脉冲值
//如果已知电机每转一圈的脉冲数为U，又已知轮子的直径D，那么就可以通过脉冲计数的方式得知每个轮子行进的距离
//比如读到电机1的脉冲总数为P，那么行进的距离为(P/U) * (3.14159*D)
//对于不同的电机可以自行测试每圈的脉冲数U，可以手动旋转10圈读出脉冲数，然后取平均值得出


//电机类型具体值
#define MOTOR_TYPE_WITHOUT_ENCODER        0  //无编码器的电机
#define MOTOR_TYPE_TT                     1  //TT编码电机
#define MOTOR_TYPE_N20                    2  //n20编码电机
#define MOTOR_TYPE_JGB37_520_12V_110RPM   3 //磁环每转是44个脉冲   减速比:90  默认

u8 data&#091;20];
bool WireWriteByte(uint8_t val)
{
    Wire.beginTransmission(I2C_ADDR);//开始一次数据传输，发送一个I2C开始字符0x34
    Wire.write(val);//像从机发送数据
    if( Wire.endTransmission() != 0 ) { //返回值为0才成功，否则失败
        return false;
    }
    return true;
}
//向地址中写入数据 （reg：地址 val：数据内容 len：数据长度）
bool WireWriteDataArray(  uint8_t reg,uint8_t *val,unsigned int len)
{
    unsigned int i;

    Wire.beginTransmission(I2C_ADDR);
    Wire.write(reg);//发送地址数据
    for(i = 0; i &lt; len; i++) {
        Wire.write(val&#091;i]);//发送内容
    }
    if( Wire.endTransmission() != 0 ) {//结果判断
        return false;
    }

    return true;
}
//读取地址中的数据（reg：地址 val：数据内容）
bool WireReadDataByte(uint8_t reg, uint8_t &amp;val)
{
    if (!WireWriteByte(reg)) {//如果WireWriteByte（）非真
        return false;
    }
    
    Wire.requestFrom(I2C_ADDR, 1);//主设备请求从设备I2C_ADDR地址处的一个字节，这个字节可以被主设备用 read()或available()接受
    while (Wire.available()) //接收获取的字节
    {
        val = Wire.read();//Wire.requestFrom()请求从机数据后，可以使用read接收
    }
    
    return true;
}
//读取地址中指定长度的数据（reg：地址 val：数据内容 len：数据长度）
int WireReadDataArray(   uint8_t reg, uint8_t *val, unsigned int len)
{
    unsigned char i = 0;
    
    /* Indicate which register we want to read from */
    if (!WireWriteByte(reg)) {
        return -1;
    }
    
    /* Read block data */
    Wire.requestFrom(I2C_ADDR, len);
    while (Wire.available()) {
        if (i &gt;= len) {
            return -1;
        }
        val&#091;i] = Wire.read();
        i++;
    }
    
    return i;
}


int serial_putc( char c, struct __file * )
{
  Serial.write( c );//Serial.print() 发送的是字符，Serial.write() 发送的字节.
  return c;
}
void printf_begin(void)
{
  fdevopen( &amp;serial_putc, 0 );//将函数serial_putc指向串口
}




uint8_t MotorType = MOTOR_TYPE_TT;  //设定电机类型
uint8_t MotorEncoderPolarity = 1; 
void setup()
{
  Wire.begin();//初始化 I2C，以 Ardunio UNO 为例 I2C 口为：A4(SCL)、A5(CLK)
  Serial.begin(9600);
  printf_begin();
  delay(200);
  WireWriteDataArray(MOTOR_TYPE_ADDR,&amp;MotorType,1);//在电机类型地址中写入电机类型编号
 //向地址中写入数据 （reg：地址 val：数据内容 len：数据长度）
 
  delay(5);
  WireWriteDataArray(MOTOR_ENCODER_POLARITY_ADDR,&amp;MotorEncoderPolarity,1);//向编码方向极性地址中写入MotorEncoderPolarity地址，长度为1

}
/*用数组传递电机速度，正数为设置前进速度，负数为设置后退速度
以 p1、p2 为例：p1=4 个电机以 50 的速度前进 p2=4 个电机以 20 的速度后退*/
int8_t p1&#091;4]={50,50,50,50};
int8_t p2&#091;4]={-50,-50,-50,-50};
int8_t s1&#091;4]={2,2,2,2};
int8_t s2&#091;4]={-2,-2,-2,-2};
//int8_t s1&#091;4]={2,20,10,30};
//int8_t s2&#091;4]={-20,-20,-30,-9};
int32_t EncodeTotal&#091;4]; //用于暂存电机累积转动量的值，正转递增，反转递减
int8_t result&#091;4]={0,0,0,0};
void loop()
{
  u16 v;  //用于暂存电压值
  WireReadDataArray(ADC_BAT_ADDR,data,2);   //读取电压地址中存放的电压
  v = data&#091;0]+ (data&#091;1]&lt;&lt;8); //将电压转换为 mV
  Serial.print("V = ");Serial.print(v);Serial.println("mV     "); //打印电压的值

  WireReadDataArray(MOTOR_ENCODER_TOTAL_ADDR,(uint8_t*)EncodeTotal,16);  //读取电机累积转动量
  printf("Encode1 = %ld  Encode2 = %ld  Encode3 = %ld  Encode4 = %ld  \r\n", EncodeTotal&#091;0], EncodeTotal&#091;1], EncodeTotal&#091;2], EncodeTotal&#091;3]);
//打印四个电机的累积转动量
  
/*在电机转速地址中写入电机的转动方向和速度：WireWriteDataArray（转速控制地址，电
机转速数组，电机个数）*/
//  WireWriteDataArray(MOTOR_FIXED_PWM_ADDR,p1,4);//固定pwm控制，开环，属于开环控制,范围（-100~100）
  WireWriteDataArray(MOTOR_FIXED_SPEED_ADDR,s1,4);//固定转速控制，闭环，单位：脉冲数每10毫秒，范围（根据具体的编码电机来，受编码线数，电压大小，负载大小等影响，一般在±50左右）
  delay(700);

//  WireWriteDataArray(MOTOR_FIXED_PWM_ADDR,p2,4);
  WireWriteDataArray(MOTOR_FIXED_SPEED_ADDR,s2,4);
  delay(700);

  //读取4个电机10ms内的脉冲数据
  for(int i=0;i&lt;10;i++)
  {
  WireReadDataArray(MOTOR_FIXED_SPEED_ADDR,result,4);
  printf("10ms内的脉冲数据为： %ld ， %ld  ， %ld ， %ld  \r\n", result&#091;0], result&#091;1], result&#091;2], result&#091;3]); 
  delay(2000);
  }
}</code></pre>



<p></div></div>



<h4 class="wp-block-heading">ros_arduino_bridge</h4>



<p>上述代码只有驱动板与arduino交互的驱动，对于arduino与ros的交互，则需要自己编写才行，好在有相关的开源项目，因此在上面进行二次开发即可。我只使用其中的和ros进行交互的部分，对于其中的底盘控制，舵机控制则不使用。</p>



<div class="wp-block-argon-github github-info-card card shadow-sm github-info-card-full" data-author="hbrobotics" data-project="ros_arduino_bridge"><div class="github-info-card-header"><a href="https://github.com/" target="_blank" title="Github" rel="noopener"><span><i class="fa fa-github"></i> GitHub</span></a></div><div class="github-info-card-body"><div class="github-info-card-name-a"><a href="https://github.com/hbrobotics/ros_arduino_bridge" target="_blank" rel="noopener"><span class="github-info-card-name">hbrobotics/ros_arduino_bridge</span></a></div><div class="github-info-card-description"></div></div><div class="github-info-card-bottom"><span class="github-info-card-meta github-info-card-meta-stars"><i class="fa fa-star"></i> <span class="github-info-card-stars"></span></span><span class="github-info-card-meta github-info-card-meta-forks"><i class="fa fa-code-fork"></i> <span class="github-info-card-forks"></span></span></div></div>



<h4 class="wp-block-heading"><strong>rosserial arduino</strong></h4>



<p>ros_arduino-bridge问题较多，换成研究下<strong>rosserial arduino</strong>试试</p>



<p><strong>1.安装Arduino</strong>（树莓派没安装桌面环境，这步可以忽略，编译上传在安装桌面环境的电脑上进行即可）</p>



<p><strong><code>sudo apt-get install arduino</code></strong></p>



<p><strong>2.启动arduino并进行相关设置</strong>（树莓派没安装桌面环境，这步可以忽略，编译上传在安装桌面环境的电脑上进行即可）</p>



<p><strong><code>arduino</code></strong></p>



<p><strong>3.安装软件包rosserial arduino</strong></p>



<p><strong><code>sudo apt-get install ros-melodic-rosserial-arduino</code><br><code>sudo apt-get install ros-melodic-rosserial</code></strong></p>



<p><strong>4.编译ros_lib文件，并复制到arduino libraries里</strong>（这里是负责编译和上传的电脑的arduino的库里，我的是Windows上的，压缩成zip,然后导入库）</p>



<p>编译：</p>



<pre class="wp-block-code"><code>rosrun rosserial_arduino make_libraries.py &#091;这里写输出目录，中括号要去掉]</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="907" height="213" src="http://daoker.cc/wp-content/uploads/2022/12/image-10.png" alt="" class="wp-image-835" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-10.png 907w, https://daoker.cc/wp-content/uploads/2022/12/image-10-300x70.png 300w, https://daoker.cc/wp-content/uploads/2022/12/image-10-768x180.png 768w" sizes="auto, (max-width: 907px) 100vw, 907px" /></figure>



<p>在windows上下载下来</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/12/image-11.png" alt="" class="wp-image-836" width="690" height="300" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-11.png 885w, https://daoker.cc/wp-content/uploads/2022/12/image-11-300x131.png 300w, https://daoker.cc/wp-content/uploads/2022/12/image-11-768x334.png 768w" sizes="auto, (max-width: 690px) 100vw, 690px" /></figure>



<p>然后压缩成zip,在arduino里导入</p>



<p><strong>5.测试，创建helloworld话题</strong></p>



<pre class="wp-block-code"><code>#include &lt;ros.h&gt;
#include &lt;std_msgs/String.h&gt;
ros::NodeHandle nh;
std_msgs::String str_msg;
ros::Publisher chatter("chatter", &amp;str_msg);
char hello&#091;13] = "hello world!";
void setup()
{
  nh.initNode();
  nh.advertise(chatter);
}
void loop()
{
  str_msg.data = hello;
  chatter.publish( &amp;str_msg );
  nh.spinOnce();
  delay(1000);
}</code></pre>



<div class="wp-block-media-text alignwide is-stacked-on-mobile"><figure class="wp-block-media-text__media"><img loading="lazy" decoding="async" width="500" height="777" src="http://daoker.cc/wp-content/uploads/2022/12/image-12.png" alt="" class="wp-image-837 size-full" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-12.png 500w, https://daoker.cc/wp-content/uploads/2022/12/image-12-193x300.png 193w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure><div class="wp-block-media-text__content">
<p>然后插入arduino编译上传后把arduino插到树莓派上</p>
</div></div>



<p>接着在树莓派上分别开终端运行</p>



<p>终端1：</p>



<pre class="wp-block-code"><code>roscore</code></pre>



<p>终端2：（这里硬件端口根据实际情况修改）</p>



<pre class="wp-block-code"><code>rosrun rosserial_python serial_node.py /dev/ttyACM0</code></pre>



<p>终端3：</p>



<pre class="wp-block-code"><code>rostopic echo /chatter</code></pre>



<p>如下图</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="529" src="http://daoker.cc/wp-content/uploads/2022/12/image-9-1024x529.png" alt="" class="wp-image-834" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-9-1024x529.png 1024w, https://daoker.cc/wp-content/uploads/2022/12/image-9-300x155.png 300w, https://daoker.cc/wp-content/uploads/2022/12/image-9-768x397.png 768w, https://daoker.cc/wp-content/uploads/2022/12/image-9-1536x794.png 1536w, https://daoker.cc/wp-content/uploads/2022/12/image-9.png 1856w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>参考文章</p>
<cite>https://www.guyuehome.com/38503</cite></blockquote>



<h4 class="wp-block-heading">实测电机参数</h4>



<p>经过实际测试，电机数据和淘宝上以及卖家给的说明书都不太一致（淘宝的坑？），但是应该可以用。</p>



<figure class="wp-block-table"><table><tbody><tr><td>平均每10圈的脉冲数</td><td>5850</td></tr><tr><td>每一圈脉冲数</td><td>585</td></tr><tr><td>轮子直径</td><td>67.5mm</td></tr><tr><td>轮子周长</td><td>212.06mm</td></tr><tr><td>两个脉冲之间，小车前进距离</td><td>0.36mm</td></tr></tbody></table></figure>



<p>知道以上参数，可以编写函数来实现测速，即单位位移除以单位时间。</p>



<h4 class="wp-block-heading">测速函数如下：</h4>



<pre class="wp-block-code"><code>long start_time = millis();//一个计算周期的开始时刻，初始值为 millis();
long interval_time = 50;//一个计算周期 50ms
double current_vel&#091;4];
int32_t start_EncodeTotal&#091;4];//构造用于储存开始时的脉冲数的数组
int32_t current_EncodeTotal&#091;4];//构造用于储存当前脉冲数的数组
void get_current_vel()
{
  long right_now = millis();  
  long past_time = right_now - start_time;//计算逝去的时间
   
  if(past_time &gt;= interval_time){//如果逝去时间大于等于一个计算周期
    //1.禁止中断
   // noInterrupts();
    //2.计算转速 转速单位可以是秒，也可以是分钟... 自定义即可
    WireReadDataArray(MOTOR_ENCODER_TOTAL_ADDR,(uint8_t*)current_EncodeTotal,16);//获取当前的转动量
    for(int i=0;i&lt;4;i++)
    {
      current_vel&#091;i]=(current_EncodeTotal&#091;i]-start_EncodeTotal&#091;i])*0.3625/past_time;//求当前各个轮子的速度，单位m/s
     
    }
    //3.重置计数器
        for(int i=0;i&lt;4;i++)
    {
      start_EncodeTotal&#091;i]=current_EncodeTotal&#091;i];//求当前各个轮子的速度，单位m/s
    }
    //4.重置开始时间
    start_time = right_now;
    //5.重启中断
   // interrupts();

     Serial.println("四个轮子的速度分别为：");
     Serial.print(current_vel&#091;0]);Serial.print("\t");Serial.print(current_vel&#091;1]);Serial.print("\t");Serial.print(current_vel&#091;2]);Serial.print("\t");Serial.print(current_vel&#091;3]);Serial.println("\t");

  }
}</code></pre>



<p>同时在setup中加入以下代码，获取开始时的转动脉冲数</p>



<pre class="wp-block-code"><code> WireReadDataArray(MOTOR_ENCODER_TOTAL_ADDR,(uint8_t*)start_EncodeTotal,16);//获取开始时的转动量</code></pre>



<p>编码器重置函数和各个电机速度控制函数如下</p>



<pre class="wp-block-code"><code>void rest_encode()//编码器计数清零
{
   EncodeTotal&#091;0]=0,EncodeTotal&#091;1]=0,EncodeTotal&#091;2]=0,EncodeTotal&#091;3]=0;//数组赋值0
  WireWriteDataArray(MOTOR_ENCODER_TOTAL_ADDR,(uint8_t*)EncodeTotal,16);//把驱动板上的编码器计数器归0
  start_EncodeTotal&#091;0]=0;start_EncodeTotal&#091;1]=0;start_EncodeTotal&#091;2]=0;start_EncodeTotal&#091;3]=0;//避免清0后第一次测速数据异常
}
void contrl_vel(int16_t *val)                                             //指定各轮的速度，单位：m/s
{//要把速度转化成脉冲数每10ms,result=val*10/0.3625
  int8_t vel2pulse&#091;4]={0,0,0,0};
  for(int i=0;i&lt;4;i++){
    //vel2pulse&#091;i]=val&#091;i]/100/0.3625;//这里会先把val&#091;i]/100取整型再除以0.3625
    vel2pulse&#091;i]=val&#091;i]/36.25;
  }
    WireWriteDataArray(MOTOR_FIXED_SPEED_ADDR,vel2pulse,4);//由于输入的数据只能为整型，控制精度低，后期考虑自己写pid,用pwm
      Serial.println("四个轮子的目标速度（单位mm/s）为：");
     Serial.print(val&#091;0]);Serial.print("\t");Serial.print(val&#091;1]);Serial.print("\t");Serial.print(val&#091;2]);Serial.print("\t");Serial.print(val&#091;3]);Serial.println("\t");
      Serial.println("四个轮子的目标脉冲(单位脉冲/10ms)为：");
     Serial.print(vel2pulse&#091;0]);Serial.print("\t");Serial.print(vel2pulse&#091;1]);Serial.print("\t");Serial.print(vel2pulse&#091;2]);Serial.print("\t");Serial.print(vel2pulse&#091;3]);Serial.println("\t");
     for(int i=0;i&lt;4;i++)
     {
     delay(2000);
     get_current_vel();
     }
}
</code></pre>



<p>上面我们把各个轮子单位时间内的脉冲数转化成了小车各个轮子单位时间内能行进的距离，但是要想让机器人运动，我们还是要进行机器人的运动学分析。</p>



<h4 class="wp-block-heading">机器人运动学分析</h4>



<p>参考文献1：https://mp.weixin.qq.com/s/Fzrpn5_3TB6apqG2Ds1v5Q</p>



<p>参考文献2：王雪松. 四驱轮式移动机器人运动控制系统研究与设计[D].上海电机学院,2016.</p>



<p>把机器人的目标速度转换成每个电机的目标速度，这个叫做机器人的运动学分析。</p>



<p>通过机器人各轮的速度求解出机器人沿X、Y、Z轴方向的速度，叫做运动学<strong>正解</strong>；</p>



<p>通过机器人沿X、Y、Z轴方向的速度求解出机器人各轮的速度，叫做运动学<strong>逆解</strong>。</p>



<div class="wp-block-argon-collapse collapse-block shadow-sm collapsed hide-border-left" style="border-left-color:#ffffff00"><div class="collapse-block-title" style="background-color:#ffffff00"><span class="collapse-block-title-inner">卖家资料</span><i class="collapse-icon fa fa-angle-down"></i></div><div class="collapse-block-body" style="display: none"><img loading="lazy" decoding="async" width="599" height="849" src="http://daoker.cc/wp-content/uploads/2022/11/image-35.png" alt=""><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-36.png" alt=""></div></div>



<p>细看卖家的最后的四个式子感觉推导的有点尴尬，好在通过阅读资料，我做了简化，控制时保证左侧轮子速度一致，右侧轮子速度也一致，就可以简化为两轮差速控制，如下：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-37.png" alt=""><br>&nbsp;<strong>简化正运动学模型</strong>是基于虚拟左右驱动轮的速度来计算几何质心COM的速度，可表示为<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-38.png" alt=""><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>简化逆运动学模型</strong>是基于几何质心COM的速度分解出左右驱动轮的速度，可表示为<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-39.png" alt=""><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;若采用公式（9-10）来描述四轮驱动移动机器人(SSMR)，这里有两个点需要进一步讨论：<br><strong>问题：在正运动学模型（9）中，左右虚拟轮的线速度vl和vr如何得到？</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;根据公式（7）可知，两左（或右）侧轮的纵向线速度大小与左（或右）虚拟轮的线速度相同。但两轮的实际转速可能不同。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从简化实际运动控制的角度出发，两左（或右）侧轮应该尽可能保持一致。这里做一个假象实验：若机器人的两前轮的速度相同，两后轮的速度相同，但前后轮的速度不同，机器人肯定是以介于前轮速度和后轮速度之间的某一速度直线运动，可想象前轮拖着后轮加快运动，后轮扯着前轮减缓运动。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所以无论前后轮的速度如何变化，机器人只会以一个速度运动，由于前后轮速度不相等会引起轮胎与地面在纵向上同时存在滚动和滑动摩擦，这会加速磨损轮胎，且不利于准确运动控制。所以，<strong>结论是尽可能保持两左（或右）侧轮速度相同</strong>，用数学语言描述就是：、<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-40.png" alt=""><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;式中，[<em>w</em>M1&nbsp;<em>w</em>M2&nbsp;<em>w</em>M3&nbsp;<em>w</em>M4]表示对应轮子的转动角速度，[<em>R</em>M1&nbsp;<em>R</em>M2&nbsp;<em>R</em>M3&nbsp;<em>R</em>M4]表示对应的轮子的半径。<br><strong>4</strong><br><strong>问题：虚拟轮间距dLR具体是多少？</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这也是四轮驱动移动机器人(SSMR)简化模型最难的一个参数，是一个随着工况变化的参数，且无法求得解析解。在参考文献[2]中给出的方法，是引入了无量纲参数<em>γ</em>：<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-41.png" alt=""><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;式中，<em>dwb</em>表示机器人的轮间距。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;问题则转化为如何求<em>γ</em>，该参数与机器人的总负载、轮胎与地面的相对摩擦系数、转弯半径及质心位置都是有关系，是一个非常复杂的参数，所以常用的方法就是做实验，控制不再改动的机器人在特定地面上做差速转向运动，采集到多组实验数据后，拟合估算<em>γ</em>。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;再回过头来看四轮驱动移动机器人(SSMR)运动学模型，基于公式（9-12）可知<br><strong>正运动学模型</strong>为<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-42.png" alt=""><br><strong>逆运动学模型</strong>为<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/11/image-43.png" alt=""></cite></blockquote>



<h4 class="wp-block-heading">踩坑</h4>



<p>1.断电时一般先断开arduino和上位机（电脑或者树莓派的连线），再断开驱动板和电池的连线，特别是电机失控，要断电急停的时候，否则可能导致驱动板损坏。</p>



<p>2.arduino可以给驱动板供电，驱动板也可以给反向给arduino供电，不知道我是不是在某次操作中，进行了1中的操作，后来发觉驱动板不能给arduino供电了，检查原来是r如下图，重新在后面5v标注的焊点处焊上排针，耶，也可以用。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" src="http://daoker.cc/wp-content/uploads/2022/12/image-3-768x1024.png" alt="" class="wp-image-805" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-3-768x1024.png 768w, https://daoker.cc/wp-content/uploads/2022/12/image-3-225x300.png 225w, https://daoker.cc/wp-content/uploads/2022/12/image-3-1152x1536.png 1152w, https://daoker.cc/wp-content/uploads/2022/12/image-3.png 1440w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>



<p>3.不知道是不是1的操作还把SCL 和SDA的上拉电阻给烧了（新板在路上，保险起见，又买了块），在调试程序中，发现<a href="https://arduino.stackexchange.com/questions/5339/wire-endtransmission-hangs">Wire.endTransmission()&nbsp;</a>函数无响应，并且是间歇性出现，有时重新断开所有电源好了，但是过一会又死在那，然后查了资料（https://arduino.stackexchange.com/questions/5339/wire-endtransmission-hangs），把SCL 和SDA外接一个4.7K的5V上拉电阻，貌似再也没有卡住了</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="897" height="499" src="http://daoker.cc/wp-content/uploads/2022/12/image-5.png" alt="" class="wp-image-807" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-5.png 897w, https://daoker.cc/wp-content/uploads/2022/12/image-5-300x167.png 300w, https://daoker.cc/wp-content/uploads/2022/12/image-5-768x427.png 768w" sizes="auto, (max-width: 897px) 100vw, 897px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/12/image-6.png" alt="" class="wp-image-808" width="477" height="192" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-6.png 693w, https://daoker.cc/wp-content/uploads/2022/12/image-6-300x121.png 300w" sizes="auto, (max-width: 477px) 100vw, 477px" /></figure>
</div>
</div>
</div>
</div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="461" src="http://daoker.cc/wp-content/uploads/2022/12/7048C8GQXD26Z9JI2KV4-1024x461.jpg" alt="" class="wp-image-809" srcset="https://daoker.cc/wp-content/uploads/2022/12/7048C8GQXD26Z9JI2KV4-1024x461.jpg 1024w, https://daoker.cc/wp-content/uploads/2022/12/7048C8GQXD26Z9JI2KV4-300x135.jpg 300w, https://daoker.cc/wp-content/uploads/2022/12/7048C8GQXD26Z9JI2KV4-768x346.jpg 768w, https://daoker.cc/wp-content/uploads/2022/12/7048C8GQXD26Z9JI2KV4-1536x691.jpg 1536w, https://daoker.cc/wp-content/uploads/2022/12/7048C8GQXD26Z9JI2KV4-2048x922.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>4.自带的示例程序里pwm控制根本没用，轮子只是抖一下，我以为是这板子太垃圾，不支持，于是就用了他的闭环控制，然后我自己又套了层PID，相当于套了2层PID,反应慢。突然我想起了，或许他示例的pwm只是给个脉冲，用循环持续的给脉冲，不就可以了吗，改了下，果真可以。唉，基础不牢，地动山摇。</p>



<p>5.但是使用4中的pwm控制发觉不能低速，最低速度也五秒3米左右，远高于试验要求，然后使用自带的闭环控制，速度可以慢一点，但是还是偏大（整体来说，买的电机减速比偏低），无奈，只能更改ros-arduino-bridge里的pid函数了</p>



<p>6.发觉就算加了上拉电阻，也会卡死，嗐，等新驱动板到了再试</p>



<p>7.为了验证是否是因为基于wire.h库的I2c挂起导致ros_arduino_bridge无法连接，编译上传不含有i2c的ros_arduino_bridge项目到arduino试试。经验证，果真是，嗐，卖家配的示例真坑</p>



<p>8.使用github另外一位大佬改编的IC2库（https://github.com/rambo/I2C），成功移植之前的代码，然后成功连接，现在就是改ros_arduino_python里的关于pid的代码，把他屏蔽掉就行</p>



<p>9.实测在连接书梅派串口后，i2c总线会卡死，导致连接不上，就算使用了新的I2C.h库，超时可以跳出，可以连接上，但三仍然功能不正常，暂时找不到解决方案，时间关系，以后再研究。</p>



<p>10.问题解决，arduino的供电来自树莓派，驱动板的供电来自电源，两个之前的vcc不用连接，连接gnd即可，在arduino程序启动的时候可以先不建立I2c通信，后面再初始话硬件然后通信</p>



<h2 class="wp-block-heading">控制系统</h2>



<p>使用ros控制电机转动从而控制小车运动，之前尝试有太多bug,并且也不是我试验的重点，花大量时间不值得，所以就修改为使用蓝牙遥控器控制，和arduino上的驱动系统相集成，因此arduino上一共集成了以下代码：I2C通信代码    红外控制电机代码   ros_arduino_bridge代码</p>



<p>首先，检测遥控器的按键编码：（灯没必要接，接好红外传感器就行）</p>



<div  class='collapse-block shadow-sm collapse-block-transparent collapsed hide-border-left'><div class='collapse-block-title'><i class='fa fa-flag'></i> <span class='collapse-block-title-inner'>遥控器按键编码代码内容过长，点击展开</span><i class='collapse-icon fa fa-angle-down'></i></div><div class='collapse-block-body' style='display:none;'></p>



<pre class="wp-block-code"><code>#include &lt;IRremote.h&gt;
int RECV_PIN = 11;
int LED1 = 2;
int LED2 = 3;
int LED3 = 4;
int LED4 = 5;
int LED5 = 6;
int LED6 = 7;
long on1  = 0x00FFA25D;//1
long off1 = 0x00FFE01F;
long on2 = 0x00FF629D;//2
long off2 = 0x00FFA857;
long on3 = 0x00FFE21D;//3
long off3 = 0x00FF906F;
long on4 = 0x00FF22DD;
long off4 = 0x00FF6897;
long on5 = 0x00FF02FD;//5
long off5 = 0x00FF9867;
long on6 = 0x00FFC23D;
long off6 = 0x00FFB04F;
IRrecv irrecv(RECV_PIN);
decode_results results;
// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
// void * to work around compiler issue
//void dump(void *v) {
//  decode_results *results = (decode_results *)v
void dump(decode_results *results) {
  int count = results-&gt;rawlen;
  if (results-&gt;decode_type == UNKNOWN)
  {
    Serial.println("Could not decode message");
  }
  else
  {
    if (results-&gt;decode_type == NEC)
    {
      Serial.print("Decoded NEC: ");
    }
    else if (results-&gt;decode_type == SONY)
    {
      Serial.print("Decoded SONY: ");
    }
    else if (results-&gt;decode_type == RC5)
    {
      Serial.print("Decoded RC5: ");
    }
    else if (results-&gt;decode_type == RC6)
    {
      Serial.print("Decoded RC6: ");
    }
    Serial.print(results-&gt;value, HEX);
    Serial.print(" (");
    Serial.print(results-&gt;bits, DEC);
    Serial.println(" bits)");
  }
  Serial.print("Raw (");
  Serial.print(count, DEC);
  Serial.print("): ");

  for (int i = 0; i &lt; count; i++)
  {
    if ((i % 2) == 1) {
      Serial.print(results-&gt;rawbuf&#091;i]*USECPERTICK, DEC);
    }
    else
    {
      Serial.print(-(int)results-&gt;rawbuf&#091;i]*USECPERTICK, DEC);
    }
    Serial.print(" ");
  }
  Serial.println("");
}

void setup()
{
  pinMode(RECV_PIN, INPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(LED5, OUTPUT);
  pinMode(LED6, OUTPUT);
  pinMode(13, OUTPUT);
  Serial.begin(9600);

  irrecv.enableIRIn(); // Start the receiver
}

int on = 0;
unsigned long last = millis();

void loop()
{
  if (irrecv.decode(&amp;results))
  {
    // If it's been at least 1/4 second since the last
    // IR received, toggle the relay
    if (millis() - last &gt; 250)
    {
      on = !on;
      //       digitalWrite(8, on ? HIGH : LOW);
      digitalWrite(13, on ? HIGH : LOW);
      dump(&amp;results);
    }
    if (results.value == on1 )
    {
      Serial.println("1号键");
      digitalWrite(LED1, HIGH);
    }
    if (results.value == off1 )
    {
      digitalWrite(LED1, LOW); Serial.println("2号键");
    }
    if (results.value == on2 )
    {
      digitalWrite(LED2, HIGH); Serial.println("3号键");
    }
    if (results.value == off2 )
    {
      digitalWrite(LED2, LOW); Serial.println("4号键");
    }
    if (results.value == on3 )
    {
      digitalWrite(LED3, HIGH); Serial.println("5号键");
    }
    if (results.value == off3 )
      digitalWrite(LED3, LOW);
    if (results.value == on4 )
      digitalWrite(LED4, HIGH);
    if (results.value == off4 )
      digitalWrite(LED4, LOW);
    if (results.value == on5 )
      digitalWrite(LED5, HIGH);
    if (results.value == off5 )
      digitalWrite(LED5, LOW);
    if (results.value == on6 )
      digitalWrite(LED6, HIGH);
    if (results.value == off6 )
      digitalWrite(LED6, LOW);
    last = millis();
    irrecv.resume(); // Receive the next value
  }
}</code></pre>



<p></div></div>



<p>然后编写控制逻辑代码，最后集成的时候在相关地方加入控制函数即可</p>



<pre class="wp-block-code"><code>#include &lt;IRremote.h&gt;
int RECV_PIN = 11;//11号引脚为数据引脚
long accelerate = 0x00FF629D;//2号数字键,前进/加速
long decelerate = 0x00FF02FD;//5号键，后退/减速
long  left=  0x00FFA25D;//1号数字键，左转
long right=  0x00FFE21D;//3号数字键，右转
long carstop =  0x00FF38C7;//停止，OK键
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup() {
pinMode(RECV_PIN, INPUT);
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}
int on = 0;
unsigned long last = millis();
void loop() {
   if (irrecv.decode(&amp;results))
  {
    // If it's been at least 1/4 second since the last
    // IR received, toggle the relay
    if (millis() - last &gt; 250)
    {
      on = !on;
      digitalWrite(13, on ? HIGH : LOW);
      //dump(&amp;results);
    }
    if (results.value == accelerate )
    {
      Serial.println("前进");
     
    }
    if (results.value == decelerate )
    {
      Serial.println("后退");
    }
    if (results.value == left )
    {
     Serial.println("左转");
    }
    if (results.value == right )
    {
      Serial.println("右转");
    }
    if (results.value == carstop )
    {
      Serial.println("停止");
    }
    last = millis();
    irrecv.resume(); // Receive the next value
  }
}</code></pre>



<h2 class="wp-block-heading">传感系统</h2>



<div class="wp-block-argon-collapse collapse-block shadow-sm collapsed hide-border-left" style="border-left-color:#ffffff00"><div class="collapse-block-title" style="background-color:#ffffff00"><span class="collapse-block-title-inner">查看局域网其他计算机ip</span><i class="collapse-icon fa fa-angle-down"></i></div><div class="collapse-block-body" style="display: none">windows:<br>以管理员身份运行cmd输入net view后回车，即可查看到自己的计算机所在的局域网内的其他计算机的计算机名<br>ping 计算机名 ，可以查到其ip<br>或者使用局域网查看工具：<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/12/image-1024x674.png" alt=""><br>ubuntu:<br>使用sudo apt-get install <a href="https://so.csdn.net/so/search?q=arp&amp;spm=1001.2101.3001.7020">arp</a>-scan 安装(确保网络链接正常)<br><br>利用ifconfig 或ip addr 查看本机的ip地址,一般有线在interface en0/eth0, 无线在wlan0上.<br>ifconfig<br>然后使用arp-scan -I wlo1 &#8211;localnet 或 sudo arp-scan -I wlo1 &#8211;localnet 就可以查看同一局域网的其他设备<br>sudo arp-scan -I wlp3s0 &#8211;localnet<br>wlp3s0换成自己的网口名<br><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/12/截图.png" alt=""><br></div></div>



<pre class="wp-block-code"><code>sudo apt install arp-scan -y</code></pre>



<pre class="wp-block-code"><code>sudo arp-scan -I wlp3s0 --localnet</code></pre>



<p>ubuntu镜像下载：http://cdimage.ubuntu.com/releases/</p>



<p>由于我用到的ros arduino bridge 不支持我的ros-noetic,所以树莓派上必须安装ros-melodic,笔记本上可以用原来的不变。而ros-melodic是在ubuntu18.04环境下，故树莓派的安装版本为：ubuntu18.04+rosmelodic,但是官方的软件刷ubuntu18.04设置得wifi不生效，于是https://blog.csdn.net/qq_30613365/article/details/120739069</p>



<h3 class="wp-block-heading">树莓派上部署ROS：</h3>



<div class="wp-block-argon-github github-info-card card shadow-sm github-info-card-full" data-author="fishros" data-project="install"><div class="github-info-card-header"><a href="https://github.com/" target="_blank" title="Github" rel="noopener"><span><i class="fa fa-github"></i> GitHub</span></a></div><div class="github-info-card-body"><div class="github-info-card-name-a"><a href="https://github.com/fishros/install" target="_blank" rel="noopener"><span class="github-info-card-name">fishros/install</span></a></div><div class="github-info-card-description"></div></div><div class="github-info-card-bottom"><span class="github-info-card-meta github-info-card-meta-stars"><i class="fa fa-star"></i> <span class="github-info-card-stars"></span></span><span class="github-info-card-meta github-info-card-meta-forks"><i class="fa fa-code-fork"></i> <span class="github-info-card-forks"></span></span></div></div>



<p>一行代码搭建机器人开发环境(ROS/ROS2/ROSDEP)</p>



<pre class="wp-block-code"><code>wget http://fishros.com/install -O fishros &amp;&amp; . fishros</code></pre>



<p>使用树莓派官方的工具刷入ubuntu20.04，刷入之前设置好用户名、密码、wifi等信息（这里我设置成收手机的，因为以后试验也是用手机共享热点当成无线路由器），同时记得打开ssh，为了方便远程连接(这里主要是不想电脑也连接手机wifi)，我们进入系统后首先安装zerotier，然后再使用上面小鱼的一键安装命令安装无桌面版ros，安装结束后添加到环境变量：</p>



<pre class="wp-block-code"><code>echo "source /opt/ros/melodic/setup.bash" &gt;&gt; ~/.bashrc
source ~/.bashrc</code></pre>



<p>然后用小鱼工具箱安装rosdepc</p>



<pre class="wp-block-code"><code>rosdepc init
rosdepc update</code></pre>



<p>最后roscore启动，没错误，ros环境部署成功。</p>



<p><a href="https://blog.csdn.net/weixin_47932709/article/details/108456635" target="_blank" rel="noreferrer noopener">树莓派配置网络出现问题之解决办法</a></p>



<h4 class="wp-block-heading">踩坑</h4>



<p>创建工作空间并初始化</p>



<p>mkdir -p field_trial/src</p>



<p>cd field_trial/</p>



<pre class="wp-block-code"><code>catkin_make</code></pre>



<p>如果提示没安装catkin，安装又发现和ros的依赖冲突，这里可以先卸载ros然后先安装catkin，再安装ros</p>



<p>1.catkin_make报错：<strong>IOError: [Errno 13] Permission denied:</strong></p>



<p><a href="https://answers.ros.org/question/246820/ros_arduino_bridge-reconnection-error/">https://answers.ros.org/question/246820/ros_arduino_bridge-reconnection-error/</a></p>



<p><a href="https://blog.csdn.net/gongdiwudu/article/details/123983828">https://blog.csdn.net/gongdiwudu/article/details/123983828</a></p>



<p>原因是创建工作空间时用了sudo或者在root下，解决方案：</p>



<pre class="wp-block-code"><code>sudo chown $USER: -R /home/pi/ros_catkin_ws</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="707" height="281" src="http://daoker.cc/wp-content/uploads/2022/12/image-7.png" alt="" class="wp-image-812" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-7.png 707w, https://daoker.cc/wp-content/uploads/2022/12/image-7-300x119.png 300w" sizes="auto, (max-width: 707px) 100vw, 707px" /></figure>



<p>2.diagnostic_updater“,CMake did not find diagnostic_updater.</p>



<p>缺少相应的依赖包，可以：</p>



<pre class="wp-block-code"><code>sudo apt-cache search diagnostics updater #在apt源里寻找同样名字的包</code></pre>



<p>根据返回安装合适的依赖包</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/12/image-8.png" alt="" class="wp-image-815" width="740" height="182" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-8.png 740w, https://daoker.cc/wp-content/uploads/2022/12/image-8-300x74.png 300w" sizes="auto, (max-width: 740px) 100vw, 740px" /></figure>



<pre class="wp-block-code"><code>sudo apt install ros-humble-diagnostic-updater</code></pre>



<h3 class="wp-block-heading">激光雷达相关</h3>



<p>连接上激光雷达，并认当前的 USB 转串口终端并修改权限，</p>



<h4 class="wp-block-heading">USB查看命令:</h4>



<pre class="wp-block-code"><code>ll /dev/ttyUSB*</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="621" height="58" src="http://daoker.cc/wp-content/uploads/2022/11/image-21.png" alt="" class="wp-image-701" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-21.png 621w, https://daoker.cc/wp-content/uploads/2022/11/image-21-300x28.png 300w" sizes="auto, (max-width: 621px) 100vw, 621px" /></figure>



<p>如果找不到，可能是数据线问题，换一根数据线即可。</p>



<p>使用</p>



<pre class="wp-block-code"><code>ls -l /dev/ttyUSB0</code></pre>



<p>发现，当前用户没有使用它的权限，因此要添加权限</p>



<pre class="wp-block-code"><code>sudo usermod -a -G dialout 你的用户名</code></pre>



<p>之后要重启才能生效</p>



<p>由于本次试验有多个外接设备，为了不造成串口混乱，可以绑定串口设备</p>



<div data-wp-interactive="core/file" class="wp-block-file"><object data-wp-bind--hidden="!state.hasPdfPreview" hidden class="wp-block-file__embed" data="http://daoker.cc/wp-content/uploads/2022/12/ubuntu绑定串口设备.pdf" type="application/pdf" style="width:100%;height:690px" aria-label="嵌入 ubuntu绑定串口设备"></object><a id="wp-block-file--media-2d22ab73-5a0d-4fd0-a0a2-daa0d7c29151" href="http://daoker.cc/wp-content/uploads/2022/12/ubuntu绑定串口设备.pdf">ubuntu绑定串口设备</a><a href="http://daoker.cc/wp-content/uploads/2022/12/ubuntu绑定串口设备.pdf" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-2d22ab73-5a0d-4fd0-a0a2-daa0d7c29151">下载</a></div>



<h4 class="wp-block-heading">雷达驱动包安装：</h4>



<p>进入src工作空间，然后下载驱动包</p>



<pre class="wp-block-code"><code>git clone https://github.com/slamtec/rplidar_ros</code></pre>



<p>返回工作空间，并catkin_make编译，并<code>source ./devel/setup.bash</code>，把功能包里的launch文件的设备端口改成之前映射好的端口。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="710" height="174" src="http://daoker.cc/wp-content/uploads/2022/12/image-1.png" alt="" class="wp-image-795" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-1.png 710w, https://daoker.cc/wp-content/uploads/2022/12/image-1-300x74.png 300w" sizes="auto, (max-width: 710px) 100vw, 710px" /></figure>



<h4 class="wp-block-heading">测试</h4>



<p>启动驱动包里的例程，如果不报错且能正确获取到当前雷达的信息，则没问题，我这里暂时没有做分布式，树莓派上安装的是无桌面版本，所以就不能通过rviz查看。</p>



<pre class="wp-block-code"><code>roslaunch rplidar_ros rplidar.launch</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="720" height="243" src="http://daoker.cc/wp-content/uploads/2022/11/image-22.png" alt="" class="wp-image-702" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-22.png 720w, https://daoker.cc/wp-content/uploads/2022/11/image-22-300x101.png 300w" sizes="auto, (max-width: 720px) 100vw, 720px" /></figure>



<p>通过 rostopic list和rostopic echo /scan也能看到相关信息输出</p>



<h3 class="wp-block-heading">IMU相关</h3>



<p>参考资料：https://blog.csdn.net/ganjb1/article/details/118153554</p>



<p>参考项目：https://github.com/COONEO/Arduino_Jetson_nano_ROS_Car/tree/melodic/Jetson_nano_ROS_code/catkin_ws/src/imu_901</p>



<p><a href="https://www.cirmall.com/articles/35838/" target="_blank" rel="noreferrer noopener">如何将 IMU （惯性测量单元） 传感器与 Arduino 对接</a></p>



<p><a rel="noreferrer noopener" href="https://blog.csdn.net/COONEO/article/details/126271853" target="_blank">开源！手把手教你搭建Arduino+英伟达Jetson的ROS小车（中）</a></p>



<p><a href="https://blog.csdn.net/zhuoyueljl/article/details/75453808">https://blog.csdn.net/zhuoyueljl/article/details/75453808</a></p>



<p>1.在windows电脑上使用商家提供的上位机软件设置IMU参数</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="717" height="612" src="http://daoker.cc/wp-content/uploads/2022/12/QQ截图20221210121627.jpg" alt="" class="wp-image-852" srcset="https://daoker.cc/wp-content/uploads/2022/12/QQ截图20221210121627.jpg 717w, https://daoker.cc/wp-content/uploads/2022/12/QQ截图20221210121627-300x256.jpg 300w" sizes="auto, (max-width: 717px) 100vw, 717px" /></figure>



<p>2.然后把设备插到ubuntu设备的usb口上</p>



<p>安装串口功能包：</p>



<pre class="wp-block-code"><code>sudo apt-get install ros-melodic-serial</code></pre>



<p>然后创建imu信息读取解析的相关功能包（参考教程：https://blog.csdn.net/qqliuzhitong/article/details/114384297）</p>



<pre class="wp-block-code"><code>catkin_create_pkg serial_demo roscpp serial</code></pre>



<p>能再屏幕上打印后根据芯片的开发手册对串口数据进行解码，最后编写数据发布代码发布/imu</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="872" height="911" src="http://daoker.cc/wp-content/uploads/2022/12/image-14.png" alt="" class="wp-image-854" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-14.png 872w, https://daoker.cc/wp-content/uploads/2022/12/image-14-287x300.png 287w, https://daoker.cc/wp-content/uploads/2022/12/image-14-768x802.png 768w" sizes="auto, (max-width: 872px) 100vw, 872px" /></figure>



<p>相关代码如下：</p>



<div  class='collapse-block shadow-sm collapse-block-transparent collapsed hide-border-left'><div class='collapse-block-title'><i class='fa fa-flag'></i> <span class='collapse-block-title-inner'>内容过长，点击展开</span><i class='collapse-icon fa fa-angle-down'></i></div><div class='collapse-block-body' style='display:none;'></p>



<p><p class="reply-to-read">温馨提示: 此处内容需要在文末<a href="#respond" title="评论本文">评论本文</a>并刷新页面后才能查看.</p></p>



<p></div></div>



<h3 class="wp-block-heading">编码器相关</h3>



<p>我的是arduino接收编码器的信息，然后arduino再通过ros_arduino_bridge和编码器交互，在ros端，我主要使用的是ros_arduino_python 下的arduino_drive.py.</p>



<p>查看权限</p>



<pre class="wp-block-code"><code>ls -l /dev/ttyACM0</code></pre>



<p>授权（前面雷达部分已经弄过就可以不弄了）</p>



<pre class="wp-block-code"><code>sudo usermod -a -G dialout your_user_name</code></pre>



<p>ros_arduino_bridge是依赖于python-serial功能包的</p>



<pre class="wp-block-code"><code>sudo apt-get install python-serial</code></pre>



<p>在ros_arduino_msg中添加自定义编码器消息</p>



<pre class="wp-block-code"><code>Header header
int16 voltage
int32&#091;4] enc</code></pre>



<p>然后按照ros自定义消息的步骤进行配置和编译，最后在arduino_drive.py中进行解析和发布话题</p>



<div  class='collapse-block shadow-sm collapse-block-transparent collapsed hide-border-left'><div class='collapse-block-title'><i class='fa fa-flag'></i> <span class='collapse-block-title-inner'>内容过长，点击展开</span><i class='collapse-icon fa fa-angle-down'></i></div><div class='collapse-block-body' style='display:none;'></p>



<p><p class="reply-to-read">温馨提示: 此处内容需要在文末<a href="#respond" title="评论本文">评论本文</a>并刷新页面后才能查看.</p></p>



<p></div></div>



<h3 class="wp-block-heading">odom里程计</h3>



<p>odom里程计数据由编码器和imu数据融合而成，https://blog.csdn.net/baimei4833953/article/details/80768762</p>



<p>imu tool:<br>http://wiki.ros.org/imu_complementary_filter?distro=noetic</p>



<p>论文：https://www.mdpi.com/1424-8220/15/8/19302</p>



<p>两种融合的方法<br>（1） 一种简单的方法<br>从imu得到的数据为一个相对角度(主要使用yaw，roll和pitch 后面不会使用到)，使用该角度来替代由编码器计算得到的角度。<br>这个方法较为简单，出现打滑时候因yaw不会受到影响，即使你抬起机器人转动一定的角度，得到的里程也能正确反映出来</p>



<p>（2）扩展的卡尔曼滤波<br>官方提供了个扩展的卡尔曼滤波的包robot_pose_ekf，robot_pose_ekf开启扩展卡尔曼滤波器生成机器人姿态，支持</p>



<p>odom（编码器）<br>imu_data（IMU）<br>vo（视觉里程计）<br>还可以支持GPS<br>引用官方图片<br></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="777" height="922" src="http://daoker.cc/wp-content/uploads/2022/12/image-15.png" alt="" class="wp-image-871" srcset="https://daoker.cc/wp-content/uploads/2022/12/image-15.png 777w, https://daoker.cc/wp-content/uploads/2022/12/image-15-253x300.png 253w, https://daoker.cc/wp-content/uploads/2022/12/image-15-768x911.png 768w" sizes="auto, (max-width: 777px) 100vw, 777px" /></figure>



<p>chatGPT真是个神器：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="266" height="1024" src="http://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合-266x1024.png" alt="" class="wp-image-876" srcset="https://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合-266x1024.png 266w, https://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合-78x300.png 78w, https://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合-768x2958.png 768w, https://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合-399x1536.png 399w, https://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合-532x2048.png 532w, https://daoker.cc/wp-content/uploads/2022/12/imu和编码器数据融合.png 1920w" sizes="auto, (max-width: 266px) 100vw, 266px" /></figure>



<h2 class="wp-block-heading">集成</h2>



<h3 class="wp-block-heading">TF坐标变换</h3>



<p>预备知识：<a href="https://www.jianshu.com/p/cb99188fec49">https://www.jianshu.com/p/cb99188fec49</a></p>



<p>在launch文件中增加以下节点的代码：</p>



<pre class="wp-block-code"><code>&lt;node pkg="tf2_ros" type="static_transform_publisher" name="imu_transform_publisher" args="0 0 0 0 0 0 base_link imu_link 100" /&gt;
</code></pre>



<pre class="wp-block-code"><code>&lt;node pkg="tf2_ros" type="static_transform_publisher" name="imu_static_transform_publisher" args="x y z yaw pitch roll base_link imu_link 100" /></code></pre>



<h3 class="wp-block-heading">ROS std_msgs/Header 数据含义</h3>



<p><a href="https://blog.csdn.net/qq_18676517/article/details/109270525">https://blog.csdn.net/qq_18676517/article/details/109270525</a></p>



<p>创建功能包</p>



<pre class="wp-block-code"><code>catkin_create_pkg integration roscpp rospy std_msgs ros_arduino_python usb_cam rplidar_ros</code></pre>



<p>功能包下创建launch文件夹，launch文件夹中新建run.launch文件,内容如下：</p>



<pre class="wp-block-code"><code>&lt;!-- 机器人启动文件：
        1.启动底盘
        2.启动激光雷达
        3.IMU
        4.启动编码器
        5.启动odom
        6.录制数据
 --&gt;
&lt;launch&gt;
        &lt;!-- &lt;include file="$(find ros_arduino_python)/launch/arduino.launch" /&gt; --&gt;
        &lt;!-- &lt;include file="$(find usb_cam)/launch/usb_cam-test.launch" /&gt; --&gt;
         &lt;!-- 2.启动激光雷达 --&gt;
        &lt;include file="$(find rplidar_ros)/launch/rplidar.launch" /&gt;
        &lt;!-- 6.录制数据 --&gt;
        &lt;!-- &lt;node pkg="rosbag" type="record" name="record" args="/imu /odom /scan /tf /tf_static  -o /home/daoker/vscode_ws/src/sugarcane_car/bag/onece.bag"/&gt; --&gt;
        &lt;node pkg="rosbag" type="record" name="record" args="/scan   -o /home/daoker/field_trial/src/integration/bag/onece.bag"/&gt;
         &lt;node pkg="tf2_ros" type="static_transform_publisher" name="static_transform_publisher" args="0 0 0 -1.57 0 -1.57 /front_bumper_link /front_bumper_link_depth" /&gt;
    &lt;node pkg="tf" type="static_transform_publisher" name="odom_to_world" args="0.0 0.0 0.0 0 0 0.0 /odom /world 1000"/&gt;
    &lt;node pkg="rosbag" type="record" name="record" args="/imu /odom /scan /tf /tf_static  -o /home/daoker/vscode_ws/src/sugarcane_car/bag/onece.bag"/&gt;

&lt;/launch&gt;
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto695.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>小主机加固态挂网心云回本实录</title>
		<link>https://daoker.cc/daokerto474.html</link>
					<comments>https://daoker.cc/daokerto474.html#comments</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Wed, 05 Oct 2022 04:36:55 +0000</pubDate>
				<category><![CDATA[单片机]]></category>
		<category><![CDATA[搞机]]></category>
		<category><![CDATA[玩docker]]></category>
		<guid isPermaLink="false">https://daoker.cc/?p=474</guid>

					<description><![CDATA[前言 前几天在小黄鱼搞了个4005U的小主机，直接刷了openwrt，网卡为AR9382，可惜找不到无线网卡驱 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">前言</h2>



<p>前几天在小黄鱼搞了个4005U的小主机，直接刷了openwrt，网卡为AR9382，可惜找不到无线网卡驱动，于是拆开想看下换块网卡，可惜和我台式机的不兼容，比台式机的小了一半，恰巧发现背板上留了个stata支架，貌似还可以扩充个stata硬盘，然后看了下主板的接口，群里的大佬说买根ph2.0的线转接成硬盘的电源线就行，不过没有闲置的硬盘，感觉买来也大概率闲置，就不管了。</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" src="http://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005122616-768x1024.jpg" alt="" class="wp-image-476" srcset="https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005122616-768x1024.jpg 768w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005122616-225x300.jpg 225w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005122616-1152x1536.jpg 1152w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005122616-1536x2048.jpg 1536w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005122616-scaled.jpg 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1-461x1024.jpg" alt="" class="wp-image-479" width="252" height="559" srcset="https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1-461x1024.jpg 461w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1-135x300.jpg 135w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1-768x1707.jpg 768w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1-691x1536.jpg 691w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1-922x2048.jpg 922w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124149-1.jpg 1080w" sizes="auto, (max-width: 252px) 100vw, 252px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="461" height="1024" src="http://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636-461x1024.jpg" alt="" class="wp-image-480" srcset="https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636-461x1024.jpg 461w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636-135x300.jpg 135w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636-768x1707.jpg 768w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636-691x1536.jpg 691w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636-922x2048.jpg 922w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005124636.jpg 1080w" sizes="auto, (max-width: 461px) 100vw, 461px" /></figure>
</div>
</div>



<p>后来在折腾openwrt过程中知道了网心云容器魔方这个docker项目，就是把自己闲置的硬盘和宽带共享给别人进行边缘计算，然后就试着布置了下，收益有点低啊，感觉我来晚了，看贴吧说以前高。但是单口小主机真不方便，于是买了usb转网卡的接线，想扩充一个网口，然后又下单了一个可能兼容的无线网卡（有点后悔，应该加点钱买多网口小主机的），然后某次看到这个stata固态，咦这价格还挺香，买来挂网心云，假如每天收入1块5，那也只要半年就白得一块固态硬盘，真香，于是开搞，买ph2.0的线，但是看介绍说接线注意电压，我这没电压表，于是想着能不能用arduino测电压，于是找到了这个帖子：<a href="https://blog.csdn.net/HANDSome_BOY_007/article/details/122682674" target="_blank" rel="noreferrer noopener">使用arduino测量电压的超级简单的方法</a></p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005125440-1-512x1024.jpg" alt="" class="wp-image-482" width="250" height="500" srcset="https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005125440-1-512x1024.jpg 512w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005125440-1-150x300.jpg 150w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005125440-1-768x1536.jpg 768w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005125440-1-1024x2048.jpg 1024w, https://daoker.cc/wp-content/uploads/2022/10/微信图片_20221005125440-1.jpg 1073w" sizes="auto, (max-width: 250px) 100vw, 250px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" src="http://daoker.cc/wp-content/uploads/2022/10/image.png" alt="" class="wp-image-485" width="473" height="449" srcset="https://daoker.cc/wp-content/uploads/2022/10/image.png 442w, https://daoker.cc/wp-content/uploads/2022/10/image-300x284.png 300w" sizes="auto, (max-width: 473px) 100vw, 473px" /></figure>
</div>
</div>



<pre class="wp-block-code"><code>/*
 *使用俩个相同的电阻串联，并将其中一个电阻的两端分别接入arduino的A0接口与GND接口实现电压的测量
 *注意所测的电压不能超过"串联电阻数量"乘"5V"，否则可能会烧掉！！！！！！！！！！！！
 *注意所测的电压不能超过"串联电阻数量"乘"5V"，否则可能会烧掉！！！！！！！！！！！！
 *注意所测的电压不能超过"串联电阻数量"乘"5V"，否则可能会烧掉！！！！！！！！！！！！
 *注意所测的电压不能超过"串联电阻数量"乘"5V"，否则可能会烧掉！！！！！！！！！！！！
 *注意所测的电压不能超过"串联电阻数量"乘"5V"，否则可能会烧掉！！！！！！！！！！！！
 */



#define BV 0.0048875855327468   //analog将5V分成了1023个单位——5/1023
#define num 2;                  //串联所接的电阻的数量
int v;                          //定义analog所测得的数字（1~1023）
long double lv;                 //定义所测电阻的电压
double zv;                      //定义整个电路的电压
void setup() {
  Serial.begin(9600);
  pinMode(A0,INPUT);

}

void loop() {
  v = analogRead(A0);
  lv = v * BV;
  zv = lv * num;                //因为两个电阻是一样的大小，所以直接相乘就可以得到所测电压的大小
  Serial.println(zv);
}
</code></pre>



<p>后来线到了，实际也没测，想着应该只要接口对应，就不会有啥问题，于是直接插了上去，能用，就是网卡，还是找不到能用的驱动，也懒得退了，先不管了，安装网心云docker容器实例代码</p>



<h2 class="wp-block-heading">部署容器魔方</h2>



<h3 class="wp-block-heading">基于虚拟网卡的多开</h3>



<pre class="wp-block-code"><code>docker run -d --name=wxedge --restart=always --privileged --net=host --tmpfs /run --tmpfs /tmp -v /media/wxedge_storage:/storage:rw registry.hub.docker.com/onething1/wxedge:latest</code></pre>



<p>然后在浏览器输入宿主机ip:18888进入后台管理界面</p>



<p>必选参数说明：</p>



<p>&#8211;privileged 必须打开（爱快docker不支持）。</p>



<p>&#8211;net 目前只支持host（推荐）和macvlan两种网络模式（不熟悉docker和网络知识的用户不要尝试macvlan，非常复杂！）。</p>



<p>“磁盘路径”是磁盘的目录，请根据自己实际磁盘目录更改，必须挂载到容器内的/storage目录，推荐磁盘是ext4文件系统，至少需要50G以上的空间，建议是固态硬盘。</p>



<p>可选参数说明（建议新手都不要设置）：</p>



<p>-e NIC=eth0 有多网卡多线路时，可以使用该参数指定路由网卡，不配置会自动选默认路由网卡，如果是openwrt系统，可以使用-e NIC=br-lan，业务申请到和局域网同一个网段的IP。</p>



<p>-e REC=false 第一次安装设置为false时，不会启动默认任务，默认为true，仅第一次安装有效。</p>



<p>-e LISTEN_ADDR=&#8221;0.0.0.0:28888&#8243; 支持设置绑定页面的监听地址和端口，监听全地址设置-e LISTEN_ADDR=&#8221;:28888&#8243;，默认监听全地址18888端口。多开容器也可以参考用该参数，比如容器A使用参数-e LISTEN_ADDR=&#8221;:18888&#8243;，容器B使用参数 -e LISTEN_ADDR=&#8221;:28888&#8243;（已经实现同一业务可多次添加，不建议多开容器）</p>



<p>-v 磁盘路径:/var/lib/containerd 如果系统盘较小时，可以增加该参数修改安装路径，注意“磁盘路径”修改成自己的磁盘目录。</p>



<p>如果想安装第二个同样的容器实例，可以使用macvlan网络的创建方法，首先创建网络环境</p>



<pre class="wp-block-code"><code>docker network create -d macvlan --subnet=192.168.1.199/24 --gateway=192.168.1.1 -o parent=eth0 macvlan</code></pre>



<p>参数含义：</p>



<ul class="wp-block-list">
<li>&#8211;subnet=192.168.1.99/24 网段，这个地方要修改成你创建docker的设备的IP段，比如设备IP是192.168.1.200，那这个地方参数就是192.168.1.199</li>



<li>&#8211;gateway=192.168.1.1 网关，这个地方要修改成你创建docker的设备的网关</li>



<li>-o parent=etho 这个地方vmbr0要修改成你设备中使用的网卡名称</li>



<li>其他参数默认</li>
</ul>



<p>创建好macvlan网络之后</p>



<pre class="wp-block-code"><code>docker run -d --name=wxedge3 --restart=always --privileged --net=macvlan --ip=192.168.1.88 --tmpfs /run --tmpfs /tmp -v /docker/wxedge_storage3:/storage:rw registry.hub.docker.com/onething1/wxedge:latest
</code></pre>



<ul class="wp-block-list">
<li>#name=wxedge2 因为是第二个，所以添加数字2</li>



<li>#ip=192.168.3.66 macvlan网络可以指定容器IP ， 这里修改成你要指定的IP,建议指定，这样方便web管理。</li>



<li>#/media/wxedge_storage2 因为是第二个容器， 所以缓存目录要添加数字2</li>
</ul>



<h3 class="wp-block-heading">基于host改端口的多开</h3>



<p>第一个容器命令</p>



<pre class="wp-block-code"><code>docker run -d --name=wxedge1 --restart=always --privileged --net=host  --tmpfs /run --tmpfs /tmp -e REC=false -v /docker/wxedge_storage1:/storage:rw registry.hub.docker.com/onething1/wxedge:latest</code></pre>



<p>默认端口18888</p>



<p>第二个容器命令</p>



<pre class="wp-block-code"><code>docker run -d --name=wxedge5 --restart=always --privileged --net=host -e LISTEN_ADDR="192.168.1.200:28888" --tmpfs /run --tmpfs /tmp -e REC=false -v /mnt/sdb1/wxedge_storage4:/storage:rw registry.hub.docker.com/onething1/wxedge:latest</code></pre>



<p>改端口为28888</p>



<h3 class="wp-block-heading">查看业务信息文件</h3>



<p>创建容器时挂载的目录/.onething_data/task/你的cg任务名/logs/server.log，cg任务名我这边是ba开头的，不好区分的话可以手动加几个，根据后缀判断，同一业务第n个就是_n</p>



<h2 class="wp-block-heading">网络相关</h2>



<h3 class="wp-block-heading">修改docker0默认网段</h3>



<p>docker0 默认网段和我的校园网认证页面的网段冲突，导致安装docker后无法认证，从而无法上网，所以可以修改docker0网段。</p>



<p>解决办法：改路由比较办法，可以一开始就将docker配置的bip改成169.254.0.1/24，可以避免冲突。在daemon配置文件里加个&#8221;bip&#8221;:“169.254.0.1/24”，重启docker就可以了</p>



<pre class="wp-block-code"><code>&#091;root@st-dev6 ~]# vim /etc/docker/daemon.json
{
"bip":"169.254.0.1/24"
}
</code></pre>



<h3 class="wp-block-heading">NAT类型检测</h3>



<p>NAT全称“Network Address Translation”，意思是“网络地址转换”。通过NAT可以让局域网内的众多主机使用一个公网IP访问Internet。由于IPV4紧缺，家用网络大部分都在NAT环境中。目前NAT分为4个类型：NAT1、NAT2、NAT3、NAT4，具体的：</p>



<p>NAT1：Full Cone NAT（全锥形NAT）；</p>



<p>NAT2：Address <a href="https://pinpai.smzdm.com/176085/" target="_blank" rel="noreferrer noopener">R</a>estricted Cone NAT（受限锥型NAT）；</p>



<p>NAT3：Port Restricted Cone NAT（端口受限锥型NAT）；</p>



<p>NAT4：Symmetric NAT（对称型NAT）；</p>



<p>从NAT1到NAT4限制越多，而家用网络最常见的是NAT3和NAT4。怎么测试网络nat类型呢?我推荐2个实用工具：</p>



<p>Windows系统可以使用na<a rel="noreferrer noopener" href="https://pinpai.smzdm.com/54823/" target="_blank">tt</a>ypetester，如果软件默认的ST<a rel="noreferrer noopener" href="https://pinpai.smzdm.com/91711/" target="_blank">U</a>N server地址不可用，可以填写国内小米的<a rel="noreferrer noopener" href="https://www.smzdm.com/fenlei/fuwuqi/" target="_blank">服务器</a>stun.miwifi.com。点击Test按钮然后在RFC 3489可以看到NAT类型数据。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="586" height="251" src="http://daoker.cc/wp-content/uploads/2022/10/image-7.png" alt="" class="wp-image-566" srcset="https://daoker.cc/wp-content/uploads/2022/10/image-7.png 586w, https://daoker.cc/wp-content/uploads/2022/10/image-7-300x128.png 300w" sizes="auto, (max-width: 586px) 100vw, 586px" /></figure>



<p>Linux可以使用pystun3（如果是python2环境，可使用pystun），pystun3基于python可用pip安装：</p>



<p>pip install pystun3<br>pystun3</p>



<p>然后即可返回NAT类型结果：</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="627" src="http://daoker.cc/wp-content/uploads/2022/10/image-8-1024x627.png" alt="" class="wp-image-567" srcset="https://daoker.cc/wp-content/uploads/2022/10/image-8-1024x627.png 1024w, https://daoker.cc/wp-content/uploads/2022/10/image-8-300x184.png 300w, https://daoker.cc/wp-content/uploads/2022/10/image-8-768x470.png 768w, https://daoker.cc/wp-content/uploads/2022/10/image-8.png 1080w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>我把pystun3安装到我的红米2小主机上，只要连上局域网wifi，检测非常方便。</p>



<p>NAT类型主要影响<a rel="noreferrer noopener" href="https://www.smzdm.com/fenlei/youxi/" target="_blank">游戏</a>延时（数据转发次数越多延时越高，网络波动越明显），和一些远程软件的数据连接。一般来说NAT3还能接受，NAT4可以换网络服务商了。</p>



<h3 class="wp-block-heading">ubuntu修改dns设置让应用使用全锥网络</h3>



<p>在使用ubuntu过程中，我发现，即使使用以上方法检测到网络类型为full-nat，但是部署docker网心云后网络类型变成了未知，所以前期我的docker容器魔方一般都是部署在openwrt里，这样感觉多了很多不需要的动能，并且使用也不方便，后来经过不断的探索，终于有人推荐了一篇文章：<p class="reply-to-read">温馨提示: 此处内容需要在文末<a href="#respond" title="评论本文">评论本文</a>并刷新页面后才能查看.</p>，于是按照文章指导，成功解决了问题，感谢这位作者。</p>



<p>问题：<br>ubuntu18.04LTS搭建k8s coredns组件报错<br>因为/etc/resolve.conf中存在 nameserver 127.0.0.53回环地址造成循环引用<br>手动修改无果，每次重启依旧会覆盖</p>



<p>原因：<br>ubuntu17.0之后特有，systemd-resolvd服务会一直覆盖</p>



<p>解决办法：</p>



<pre class="wp-block-code"><code>sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
sudo apt install unbound
sudo rm -rf /etc/resolv.conf
sudo vim /etc/NetworkManager/NetworkManager.conf</code></pre>



<p>在[main]<br>下面添加<br>dns=unbound<br>将dns服务替换为unbound<br>reboot<br>重启电脑即可，开机查看resolve.conf发现nameserver自动配置</p>



<h3 class="wp-block-heading">WIN10/WIN11开启UPNP</h3>



<ul class="wp-block-list">
<li>1、按下“Win+R”打开运行，输入“services.msc”点击确定打开“服务”；</li>



<li>2、双击打开“Funtion Discovery Resource Publication”，将其启动类型修改为“自动”，点击应用，点击启用，点击确定；</li>



<li>3、按照此方法按顺序将“SSDP Discovery Service”、“UPnP Device Host”服务进行设置即可！</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto474.html/feed</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
		<item>
		<title>基于二维激光雷达的蔗田垄高检测研究——仿真和算法研究</title>
		<link>https://daoker.cc/daokerto738.html</link>
					<comments>https://daoker.cc/daokerto738.html#comments</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Fri, 25 Nov 2022 04:28:17 +0000</pubDate>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[ROS]]></category>
		<category><![CDATA[单片机]]></category>
		<category><![CDATA[机器人]]></category>
		<category><![CDATA[机器视觉]]></category>
		<category><![CDATA[树莓派]]></category>
		<guid isPermaLink="false">https://daoker.cc/?p=738</guid>

					<description><![CDATA[很久之前就做了，等有空再整理，先挖坑。]]></description>
										<content:encoded><![CDATA[
<p>很久之前就做了，等有空再整理，先挖坑。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto738.html/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>【arduino】4位8段数码管的使用</title>
		<link>https://daoker.cc/daokerto7.html</link>
					<comments>https://daoker.cc/daokerto7.html#respond</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Thu, 02 Jul 2020 08:22:58 +0000</pubDate>
				<category><![CDATA[单片机]]></category>
		<guid isPermaLink="false">https://daoker.eu.org/?p=7</guid>

					<description><![CDATA[参考文章 原理分析：a,b,c,d,e,f,g,dp控制8段灯，其他4针脚控制位显示。但是如何控制不同位同时显 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p><a href="https://blog.csdn.net/c80486/article/details/52622042?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.edu_weight&amp;depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.edu_weight" target="_blank" rel="noreferrer noopener">参考文章</a></p>



<p>原理分析：<br>a,b,c,d,e,f,g,dp控制8段灯，其他4针脚控制位显示。<br>但是如何控制不同位同时显示不同数字，比如第一位显示1，第二位显示二，其实实际上是不同时显示，只不过是视觉效果。我们先选择1位置，然后点亮数字1，然后熄灭1位置，打开2位置，点亮2，整个过程十分迅速，所以看起来是显示12.<br>这里用同时点亮1位置b段和2位置c段做测试</p>



<pre class="wp-block-code"><code>void setup() {
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
}
//使用共阳4位数码管，相应的数字位为高电平则选中，相应的段位低电平在点亮
void loop() {
digitalWrite(9,1);   //9号端口高电平
digitalWrite(2,0);	//2号端口低电平
digitalWrite(2,1);
digitalWrite(9,0);
digitalWrite(10,1);
digitalWrite(3,0);
digitalWrite(10,0);
digitalWrite(3,1);

}</code></pre>



<pre class="wp-block-code"><code>//显示23.45
int a=2;
#define w1 10
int const  num&#91;11]&#91;8]={
    {0,0,0,0,0,0,1,1},  //0
    {1,0,0,1,1,1,1,1},  //1
    {0,0,1,0,0,1,0,1},  //2
    {0,0,0,0,1,1,0,1},  //3
    {1,0,0,1,1,0,0,1},  //4
    {0,1,0,0,1,0,0,1},  //5
    {0,1,0,0,0,0,0,1},  //6
    {0,0,0,1,1,1,1,1},  //7
    {0,0,0,0,0,0,0,1},  //8
    {0,0,0,0,1,0,0,1},  //9
    {1,1,1,1,1,1,1,0},  //10
};

void setup() {
for(int i=a;i&lt;w1+4;i++){
  pinMode(i,OUTPUT);
}
Serial.begin(9600);
}
void clc(){ //清屏
  for(int i=a;i&lt;a+8;i++){
  digitalWrite(i,1); //段高电平熄灭
}
for(int i=w1;i&lt;w1+4;i++){
  digitalWrite(i,0); //位低电平熄灭
}
}
void led(int x,int y){
  clc();
  if(x>4)return;
  digitalWrite(w1+x-1,1);
  for(int i=0;i&lt;8;i++)
  digitalWrite(a+i,num&#91;y]&#91;i]);
}
void loop() {
 led(1,2);
 led(2,3);
 led(2,10);
 led(3,4);
 led(4,5);
}
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto7.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>74HC595移位寄存器</title>
		<link>https://daoker.cc/daokerto15.html</link>
					<comments>https://daoker.cc/daokerto15.html#respond</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Thu, 02 Jul 2020 08:59:00 +0000</pubDate>
				<category><![CDATA[单片机]]></category>
		<guid isPermaLink="false">https://daoker.eu.org/?p=15</guid>

					<description><![CDATA[参考文献 参考文献 1-7,15号端口接8个LED或者8位数码管；8号口接地；9号口连接下一个595或者置空； [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p><a href="https://blog.csdn.net/weixin_41445387/article/details/80500046?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159367463719725211948920%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&amp;request_id=159367463719725211948920&amp;biz_id=0&amp;utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-8-80500046.first_rank_ecpm_v3_pc_rank_v3&amp;utm_term=74hc595" target="_blank" rel="noreferrer noopener">参考文献</a></p>



<p><a href="https://blog.csdn.net/jbh18401/article/details/75334562?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159367463719725211948920%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&amp;request_id=159367463719725211948920&amp;biz_id=0&amp;utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-75334562.first_rank_ecpm_v3_pc_rank_v3&amp;utm_term=74hc595" target="_blank" rel="noreferrer noopener">参考文献</a></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="848" height="661" src="http://daoker.cc/wp-content/uploads/2022/08/image.png" alt="" class="wp-image-16" srcset="https://daoker.cc/wp-content/uploads/2022/08/image.png 848w, https://daoker.cc/wp-content/uploads/2022/08/image-300x234.png 300w, https://daoker.cc/wp-content/uploads/2022/08/image-768x599.png 768w" sizes="auto, (max-width: 848px) 100vw, 848px" /></figure>



<p>1-7,15号端口接8个LED或者8位数码管；<br>8号口接地；<br>9号口连接下一个595或者置空；<br>10号口接vcc；<br>11号口接D2；<br>11脚SRCLK移位寄存器时钟输入：当一个新的位数据要进来时，已经进入的位数据就在移位寄存器时钟脉冲的控制下，整体后移，让出位置。<br>12号口D3；<br>12脚RCLK存储寄存器：数据从位移寄存器转移到存储寄存器，也是需要时钟脉冲驱动的，这就是12脚的作用。它也是上升沿有效。<br>13号接口置空或者接GND;<br>14号接D4;<br>14脚串行输入：595的数据来源只有这一个口，一次只能输入一个位，那么连续输入8次，就可以积攒为一个字节了。<br>16号口接vcc<br></p>



<pre class="wp-block-code"><code>//淘宝店主配的代码，发觉后2个函数根本没用到，而是直接用shiftout函数
int data = 4; 
int clock = 3;
int latch = 2;
int ledState = 0;
const int ON = HIGH;
const int OFF = LOW;
void setup()
{
pinMode(data, OUTPUT);
pinMode(clock, OUTPUT);
pinMode(latch, OUTPUT);
}
void loop()
{
int delayTime = 100;
for(int i = 0; i &lt; 256; i++)
{
updateLEDs(i);
delay(delayTime);
}
}
void updateLEDs(int value)
{
digitalWrite(latch, LOW);    //移位寄存器
shiftOut(data, clock, MSBFIRST, value);
digitalWrite(latch, HIGH);
}
/*void updateLEDsLong(int value)
    {
  digitalWrite(latch, LOW);
  for(int i = 0; i &lt; 8; i++)
  {
  int bit = value &amp; B10000000;
  value = value &lt;&lt; 1;
  if(bit == 128){digitalWrite(data, HIGH);}
  else{digitalWrite(data, LOW);}
  digitalWrite(clock, HIGH);
  delay(1);
  digitalWrite(clock, LOW);
  }
  digitalWrite(latch, HIGH);
  }
int bits&#91;]={B00000001, B00000010, B00000100, B00001000, B00010000, B00100000,
B01000000, B10000000};
int masks&#91;] ={B11111110, B11111101, B11111011, B11110111, B11101111, B11011111,
B10111111, B01111111};
void changeLED(int led, int state)
{
ledState = ledState &amp; masks&#91;led];
if(state == ON){ledState = ledState | bits&#91;led];}
updateLEDs(ledState);
}*/</code></pre>



<p><a href="https://blog.csdn.net/haigear/article/details/88974906">Arduino中shiftOut怎么用？</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto15.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【arduino】esp8266&#8212;wifi模块接线和程序烧录</title>
		<link>https://daoker.cc/daokerto18.html</link>
					<comments>https://daoker.cc/daokerto18.html#respond</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Fri, 17 Jul 2020 09:09:00 +0000</pubDate>
				<category><![CDATA[单片机]]></category>
		<guid isPermaLink="false">https://daoker.eu.org/?p=18</guid>

					<description><![CDATA[折腾了好久，终于能通过arduino把程序烧录至esp8266了，看了好多教程资料，大多不全，模棱两可，不适合 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>折腾了好久，终于能通过<a rel="noreferrer noopener" href="https://so.csdn.net/so/search?q=arduino&amp;spm=1001.2101.3001.7020" target="_blank">arduino</a>把程序烧录至esp8266了，看了好多教程资料，大多不全，模棱两可，不适合初学者。<br><strong>一、引脚定义</strong><br>参考文献：<br><a href="https://blog.csdn.net/xiaoyuanwuhui/article/details/83795393">ESP8266（一）—引脚定义&amp;接线说明</a><br>他的接线部分和我的不同。我的rx-rx,tx-tx,具体有没有接对可以用程序进行测试。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="313" height="193" src="http://daoker.cc/wp-content/uploads/2022/08/image-1.png" alt="" class="wp-image-19" srcset="https://daoker.cc/wp-content/uploads/2022/08/image-1.png 313w, https://daoker.cc/wp-content/uploads/2022/08/image-1-300x185.png 300w" sizes="auto, (max-width: 313px) 100vw, 313px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="847" height="345" src="http://daoker.cc/wp-content/uploads/2022/08/image-2.png" alt="" class="wp-image-20" srcset="https://daoker.cc/wp-content/uploads/2022/08/image-2.png 847w, https://daoker.cc/wp-content/uploads/2022/08/image-2-300x122.png 300w, https://daoker.cc/wp-content/uploads/2022/08/image-2-768x313.png 768w" sizes="auto, (max-width: 847px) 100vw, 847px" /></figure>



<p><strong>二.接线说明</strong><br>参考文献：<a href="https://www.jianshu.com/p/cde9f7072eab">Arduino uno r3 使用 ESP8266 wifi 模块完整流程</a></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="687" height="350" src="http://daoker.cc/wp-content/uploads/2022/08/image-3.png" alt="" class="wp-image-21" srcset="https://daoker.cc/wp-content/uploads/2022/08/image-3.png 687w, https://daoker.cc/wp-content/uploads/2022/08/image-3-300x153.png 300w" sizes="auto, (max-width: 687px) 100vw, 687px" /></figure>



<p>这里我chpd（en)直接接3.3v也没事<br>把代码上传到arduino，测试接线正确与否。</p>



<pre class="wp-block-code"><code>const int tx = 1;
const int rx = 0;
void setup() {
  //Serial.begin(9600); 注意！！千万别加这行代码
  pinMode(rx,INPUT_PULLUP);
  pinMode(tx,INPUT_PULLUP); 

}

void loop() {

}
</code></pre>



<p>每个版本的<a href="https://so.csdn.net/so/search?q=ESP8266&amp;spm=1001.2101.3001.7020" target="_blank" rel="noreferrer noopener">ESP8266</a>波特率不同，一般是115200，你可以多测试几次。</p>



<figure class="wp-block-image"><img decoding="async" src="https://img-blog.csdnimg.cn/20200717105052233.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE4NjMzMTk=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"/></figure>



<p>SP8266 的 AT 命令可以在网上查，比如下面的这几个</p>



<pre class="wp-block-code"><code>AT+RST 重启模块
AT+GMR 查看版本信息
AT+CIOBAUD=9600 修改波特率
T+CWMODE? //查询工作模式 1.Station （客户端模式）2.AP （接入点模式）3.Station+AP （两种模式共存）
</code></pre>



<p>三.程序烧录至ESP8266<br>我看网上有的教程说要取掉arduino的啥啥啥芯片，我这个貌似不可拆，还有的是用专门的下载线和烧录软件，这个我也没有。<br>直到找到了这篇文章<br>Arduino和ESP8266-模块安装及代码上传<br>简单介绍，就是：<br>1.添加开发板管理网站http://arduino.esp8266.com/stable/package_esp8266com_index.json<br>2.在开发板管理器里安装ESP8266 Community。此步骤可能需要fq,如果没有可以下载离线包，也可以用这个帖子里分享的Arduino IDE安装esp8266 SDK（Windows10），或者自己去github下载<br>3.选择开发板，可以按照参考文献里的来配置<br>Arduino和ESP8266-模块安装及代码上传<br>Arduino烧录ESP8266模块，实现WiFi通信，连接OneNET平台（一）<br>我是选了Generic ESP8266 Module，其他都是默认没动，这里我最高版本的ARDUINO IDE里编程器下面没有选项，查了很多资料也没有这个问题的解释，所以我换了低一点版本的IDE就有了，我选skII。<br>4.烧录，在IDE里写好代码后，</p>



<p>将ESP8266设定为FLASH（烧写）模式：当GPIO0（上图紫色线）接地线时，ESP8266是以bootloader模式（编程模式）启动的，这也就是你可以烧写ESP8266（将Arduino上的代码转移到ESP8266上）的时候。在Arduino代码加载完毕时，你将看到Arduino IDE底部提示“上传结束”的信息，然后代码就会开始运行了。<br>程序上传结束后，将紫色线移除：当你不想将ESP8266一直设定为烧写模式时，将紫色线移除即可，程序将会一直在ESP8266上执行。<br>来源Arduino结合ESP8266 Serial WIFI模块访问远程服务器<br>实际操作：<br>【3】确保如上面连线图所示，A、B两条连线断开，即esp8266的EN、IO0针脚和UNO板子之间是断开的，<br>【4】点击IDE的 [上传] 按钮，开始通过UNO板子向esp8266烧写程序，<br>【5】当日志窗口出现 “Connecting…………………<strong>_</strong>” 字样时，<br>【6.1】首先，将ESP8266的IO0和UNO板子的GND连接，<br>【6.2】接着，将ESP8266的EN和UNO的3.3V连接，<br>程序继续烧写，继而完成。<br>烧写完成，提示“上传成功”，<br>【7.1】然后，断开8266的EN、IO0和板子的连接。如果不再需要和Arduino进行串口通信，TX、RX针脚也可以断开。<br>【7.2】再次将EN和3.3V连接，模块就进入工作模式了。<br>来源：Arduino和ESP8266-模块安装及代码上传</p>



<p>简单点说就是；在编译快完成时拉低IOO然后，rst低-高-低重启模块进入下载模式。<br>OK，下面可以写各种程序happy了。<br>刷at固件，连接和烧写程序一样ESP8266: Reflash Dance!<br></p>



<pre class="wp-block-code"><code>/*
    This sketch demonstrates how to set up a simple HTTP-like server.
    The server will set a GPIO pin depending on the request
      http://server_ip/gpio/0 will set the GPIO2 low,
      http://server_ip/gpio/1 will set the GPIO2 high
    server_ip is the IP address of the ESP8266 module, will be
    printed to Serial when the module is connected.
*/

#include &lt;ESP8266WiFi.h>

const char* ssid = "401-2G";
const char* password = "415408807";

// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);

void setup() {
  Serial.begin(9600);
  delay(10);

  // prepare GPIO2
  pinMode(LED_BUILTIN, OUTPUT);//在arduino里默认#define LED_BUILTIN 13，esp8266里默认为GIO2
  digitalWrite(LED_BUILTIN, 0);

  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.println(WiFi.localIP());
}
void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }

  // Wait until the client sends some data
  Serial.println("new client");
  while (!client.available()) {
    delay(1);
  }

  // Read the first line of the request
  String req = client.readStringUntil('\r');
  Serial.println(req);
  client.flush();

  // Match the request
  int val;
  Serial.printf("%d\n", req.indexOf("/gpio/0")); //查找是否包含/gpio/0，找不到返回-1
  Serial.printf("%d\n", req.indexOf("/gpio/1"));
  if (req.indexOf("/gpio/0") != -1)
    val = 0;
  else if (req.indexOf("/gpio/1") != -1)
    val = 1;
  else {
    Serial.println("invalid request");
    client.stop();
    return;
  }


  // Set GPIO2 according to the request
  digitalWrite(LED_BUILTIN, val);

  client.flush();

  // Prepare the response

  //  String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n&lt;!DOCTYPE HTML>\r\n&lt;html>\r\nGPIO is now ";
  //  s += (val) ? "high" : "low";
  //  s += "&lt;/html>\n";
  //  // Send the response to the client
  //  client.print(s);
  client.print("&lt;html>&lt;body>&lt;p>GPIO is now ");
  client.print((val) ? "high" : "low");
  client.println(" &lt;/p>");
  client.print("&lt;a href=\"http://");
  client.print(WiFi.localIP());
  client.println("/gpio/1\">开&lt;/a>");//当遇到要输出"时，为了避免和print（""),可以写成print("\"")
  client.print("&lt;a href=\"http://");
  client.print(WiFi.localIP());
  client.println("/gpio/0 \">关&lt;/a>");
  client.println("&lt;/body>&lt;/html>");

  delay(1);
  Serial.println("Client disonnected");

  // The client will actually be disconnected
  // when the function returns and 'client' object is detroyed
}
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto18.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Arduino IDE 编译上传程序至stm32</title>
		<link>https://daoker.cc/daokerto24.html</link>
					<comments>https://daoker.cc/daokerto24.html#respond</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Sat, 15 Aug 2020 12:32:00 +0000</pubDate>
				<category><![CDATA[单片机]]></category>
		<guid isPermaLink="false">https://daoker.eu.org/?p=24</guid>

					<description><![CDATA[原本想直接用arduino uno 开发板当成数据线上传程序至stm32,可是试了很久都不能成功，后来只好买了 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>原本想直接用<a rel="noreferrer noopener" href="https://so.csdn.net/so/search?q=arduino&amp;spm=1001.2101.3001.7020" target="_blank">arduino</a> uno 开发板当成数据线上传程序至stm32,可是试了很久都不能成功，后来只好买了个usb转tll下载器，首先烧录bootloader,然后再上传程序，可惜我这台机器的stm32usb驱动安装不上去，所以就用usb-tll下载玩玩。<br>参考资料：<br><a href="https://blog.csdn.net/chengshui565665/article/details/100991436/">添加链接描述</a><br><a href="https://blog.csdn.net/hk_shao/article/details/79319548?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-2-79319548.nonecase&amp;utm_term=arduino%E4%BB%A3%E6%9B%BF%20ttl">添加链接描述</a><br><a href="https://blog.csdn.net/jacka654321/article/details/82827925">添加链接描述</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto24.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>stm32注意事项</title>
		<link>https://daoker.cc/daokerto27.html</link>
					<comments>https://daoker.cc/daokerto27.html#respond</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Tue, 18 Aug 2020 12:34:00 +0000</pubDate>
				<category><![CDATA[单片机]]></category>
		<guid isPermaLink="false">https://daoker.eu.org/?p=27</guid>

					<description><![CDATA[用isp方式下载程序：用usb-tll线连接后，flymcu的设置如下： 编程时的注意事项： 写代码烧录后不运 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>用isp方式下载程序：<br>用usb-tll线连接后，flymcu的设置如下：</p>



<figure class="wp-block-image"><img decoding="async" src="https://img-blog.csdnimg.cn/20200818220819214.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE4NjMzMTk=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"/></figure>



<p>编程时的注意事项：</p>



<pre class="wp-block-code"><code>GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_13; 此处本用||，这里应该不是或的意思。

/********************************
  板载led，即pc13为低电平点亮，高电平熄灭
  **********************************/
    GPIO_SetBits(GPIOC,GPIO_Pin_13);
    GPIO_ResetBits(GPIOC, GPIO_Pin_13);  
</code></pre>



<p>写代码烧录后不运行，注意几处设置：</p>



<figure class="wp-block-image"><img decoding="async" src="https://img-blog.csdnimg.cn/20200819142418765.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE4NjMzMTk=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://img-blog.csdnimg.cn/20200819142520713.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE4NjMzMTk=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://img-blog.csdnimg.cn/20200819142547510.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTE4NjMzMTk=,size_16,color_FFFFFF,t_70#pic_center" alt="在这里插入图片描述"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto27.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
