<?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/%e6%a0%91%e8%8e%93%e6%b4%be/feed" rel="self" type="application/rss+xml" />
	<link>https://daoker.cc</link>
	<description>个人随笔小记</description>
	<lastBuildDate>Fri, 10 Nov 2023 13:05:06 +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>OpenＷrt磁盘分区扩容、网络设置、跳过开机enter、安装哪吒探针</title>
		<link>https://daoker.cc/daokerto398.html</link>
					<comments>https://daoker.cc/daokerto398.html#comments</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Sat, 10 Sep 2022 07:22:49 +0000</pubDate>
				<category><![CDATA[OpenWrt]]></category>
		<category><![CDATA[搞机]]></category>
		<category><![CDATA[树莓派]]></category>
		<category><![CDATA[玩docker]]></category>
		<guid isPermaLink="false">https://daoker.cc/?p=398</guid>

					<description><![CDATA[扩容 ext4固件扩容 &#160;Squashfs固件扩容 PVE里的OpenWRT扩容 在PVE里安装Op [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">扩容</h2>



<h3 class="wp-block-heading">ext4固件扩容</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">ext4固件扩容</span><i class="collapse-icon fa fa-angle-down"></i></div><div class="collapse-block-body" style="display: none">由于我的树莓派使用的是ext4的固件，所以网上的许多扩容方法都不能用。<br>参考文献<br>https://ost.51cto.com/posts/1673<br>把树莓派的ＳＤ卡取出来，插到Linux电脑上，我的环境是Ubuntu。<br><strong>１．确定sd卡盘符</strong><br>使用df -h命令，查看sd卡的盘符：<br><img decoding="async" src="https://dl-harmonyos.51cto.com/images/202011/27f9108895be1c00bec346e01195bd573a5d94.jpg" alt="树莓派OpenＷrt扩容磁盘分区大小-开源基础软件社区"><br>确定ｓｄ的盘符是/dev/sdc。<br><strong>２．卸载/dev/sdc2</strong><br><code>umount /dev/sdc2</code><br>复制<br><br><strong>３．用fdisk命令开始重新分区</strong><br>进入分区程序：<br><code>sudo fdisk /dev/sdc</code><br>复制<br><br>查看当前分区情况：p<br><img decoding="async" src="https://dl-harmonyos.51cto.com/images/202011/f810a2028e09cab0c2a528853309630e00f150.jpg" alt="树莓派OpenＷrt扩容磁盘分区大小-开源基础软件社区"><br>这里一定要记住/dev/sdc２分区的起点（我的是57344，等会要用到）。<br><strong>４．删除分区２</strong><br>删除分区：ｍ<br>选择删除分区２：2<br><img decoding="async" src="https://dl-harmonyos.51cto.com/images/202011/a8fc12e80b8ef47bc62875d8560e0c94acb8db.jpg" alt="树莓派OpenＷrt扩容磁盘分区大小-开源基础软件社区"><br><strong>５．重新新建分区２</strong><br>然后新建分区：ｎ<br>分区类型选择主分区:p<br>分区号输入２：２<br>起始扇区选择原来的点：57344<br>最后一个扇区选择默认：enter<br>这样就新建了一个分区２，并且分区包含了剩下的全部空间。<br><img decoding="async" src="https://dl-harmonyos.51cto.com/images/202011/841457181d890f2b0c2713a4d2b50af78985d4.jpg" alt="树莓派OpenＷrt扩容磁盘分区大小-开源基础软件社区"><br>接着提示是否移除”分区 #2 的 ext4 签名“，选择否：ｎｏ<br><strong>6．保存分区修改</strong><br>最后输入w保存刚才的分区<br><img decoding="async" src="https://dl-harmonyos.51cto.com/images/202011/077a528310f7f34afc224421cfae992a964515.jpg" alt="树莓派OpenＷrt扩容磁盘分区大小-开源基础软件社区"><br><strong>7．使修改生效</strong><br><code>sudo e2fsck /dev/sdc2 sudo resize2fs /dev/sdc2</code><br>复制<br>这样就完成了树莓派ＯpenWrt分区扩容，将ＳＤ卡重新插回树莓派，开机后就可以看到分区已经扩容成功了！<br><img decoding="async" src="https://dl-harmonyos.51cto.com/images/202011/25d31e5696fbdefff510747d977af86d0d7b07.jpg" alt="树莓派OpenＷrt扩容磁盘分区大小-开源基础软件社区"></div></div>



<h3 class="wp-block-heading">&nbsp;Squashfs固件扩容</h3>



<figure class="wp-block-image"><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/10/image-3.png" alt=""/></figure>



<pre class="wp-block-code"><code><code># 将刚刚新建的 /dev/mmcblk0p3 分区挂载至 /mnt </code>
<code>root@OpenWrt:/# mount /dev/mmcblk0p3 /mnt </code>
<code># 查看分区挂载情况 </code>
<code>root@OpenWrt:/# lsblk </code>
<code>NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 760.8M 0 loop /overlay mmcblk0 179:0 0 14.9G 0 disk </code>
<code>├─mmcblk0p1 179:1 0 64M 0 part /boot ├─mmcblk0p2 179:2 0 960M 0 part /rom └─mmcblk0p3 179:3 0 13.8G 0 part /mnt </code>
<code># 将 /overlay 分区下的所有文件拷贝至刚刚建立好的分区内 </code>
<code>root@OpenWrt:/# cp -f -a /overlay/. /mnt </code>
<code># 查看是否拷贝成功 </code>
<code>root@OpenWrt:/# ls -a /mnt </code>
<code>. .. .fs_state lost+found upper work </code>
<code>root@OpenWrt:/# ls -a /overlay </code>
<code>. .. .fs_state upper work </code>
<code># 同步文件 </code>
<code>root@OpenWrt:/# sync </code>
<code># 卸载 /dev/mmcblk0p3 分区 </code>
<code>root@OpenWrt:/# umount /mnt</code></code></pre>



<figure class="wp-block-image"><img decoding="async" src="http://daoker.cc/wp-content/uploads/2022/10/image-4.png" alt=""/></figure>



<h3 class="wp-block-heading">PVE里的OpenWRT扩容</h3>



<p>在PVE里安装OpenWRT并使用上述的两种扩容方式并不生效，由此只有另谋它法。</p>



<p>首先使用常规的方法将OpenWRT安装到PVE里，虽然我们分配了很多的硬盘，但是安装后的系统并不能用那么多，此时在op的命令行中输入如下命令：</p>



<pre class="wp-block-code"><code>opkg update
opkg install cfdisk fdisk e2fsprogs</code></pre>



<p>使用cfdisk进行扩容：</p>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="675" height="553" src="https://daoker.cc/wp-content/uploads/2023/11/image.png" alt="" class="wp-image-1181" style="width:700px;height:auto" srcset="https://daoker.cc/wp-content/uploads/2023/11/image.png 675w, https://daoker.cc/wp-content/uploads/2023/11/image-300x246.png 300w" sizes="(max-width: 675px) 100vw, 675px" /></figure>



<figure class="wp-block-image size-full"><img decoding="async" width="675" height="553" src="https://daoker.cc/wp-content/uploads/2023/11/image-1.png" alt="" class="wp-image-1182" srcset="https://daoker.cc/wp-content/uploads/2023/11/image-1.png 675w, https://daoker.cc/wp-content/uploads/2023/11/image-1-300x246.png 300w" sizes="(max-width: 675px) 100vw, 675px" /></figure>



<figure class="wp-block-image size-full"><img decoding="async" width="675" height="553" src="https://daoker.cc/wp-content/uploads/2023/11/image-2.png" alt="" class="wp-image-1183" srcset="https://daoker.cc/wp-content/uploads/2023/11/image-2.png 675w, https://daoker.cc/wp-content/uploads/2023/11/image-2-300x246.png 300w" sizes="(max-width: 675px) 100vw, 675px" /></figure>



<p>输入YES并按下回车键，写入新分区；切换 <strong>Quit</strong> ，按下回车键退出。运行 <code>fdisk -l</code> 命令，查看是否成功创建新分区。</p>



<p>然后格式化分区：</p>



<pre class="wp-block-code"><code>mkfs.ext4 /dev/sda3
</code></pre>



<p>此时可能会报错，我们可以使用下列方法解决：</p>



<ul class="wp-block-list">
<li>问题很简单，就是版本不正确，删除旧的版本 然后上传安装新的版本就行了。</li>



<li>在OpenWRT网页管理界面中，进入系统 -> 软件包 查找 e2fsprogs 点击 移除，删除自带的 e2fsprogs</li>



<li>重新下载最新版的<a href="https://mirrors.aliyun.com/openwrt/releases/21.02.3/packages/x86_64/base/e2fsprogs_1.45.6-2_x86_64.ipk">e2fsprogs </a>，我这里下载的是X86版，并上传到OpenWRT上，并使用opkg install 命令安装就行，此时应该可以执行上面的格式化命令了。</li>
</ul>



<p>进入 Open­Wrt 管理后台，依次点击 <strong>系统</strong> &#8211; <strong>挂载点</strong> 找到并点击全局设置中的 <strong>生成配置</strong> 。然后刷新</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="974" height="671" src="https://daoker.cc/wp-content/uploads/2023/11/image-3.png" alt="" class="wp-image-1184" srcset="https://daoker.cc/wp-content/uploads/2023/11/image-3.png 974w, https://daoker.cc/wp-content/uploads/2023/11/image-3-300x207.png 300w, https://daoker.cc/wp-content/uploads/2023/11/image-3-768x529.png 768w" sizes="auto, (max-width: 974px) 100vw, 974px" /></figure>



<p>点击挂载点中的修改，选择根，然后复制下面命令，并应用保存，这里要按照自己的时间情况对路径进行修改</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="944" height="665" src="https://daoker.cc/wp-content/uploads/2023/11/image-4.png" alt="" class="wp-image-1185" srcset="https://daoker.cc/wp-content/uploads/2023/11/image-4.png 944w, https://daoker.cc/wp-content/uploads/2023/11/image-4-300x211.png 300w, https://daoker.cc/wp-content/uploads/2023/11/image-4-768x541.png 768w" sizes="auto, (max-width: 944px) 100vw, 944px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="975" height="343" src="https://daoker.cc/wp-content/uploads/2023/11/image-5.png" alt="" class="wp-image-1186" srcset="https://daoker.cc/wp-content/uploads/2023/11/image-5.png 975w, https://daoker.cc/wp-content/uploads/2023/11/image-5-300x106.png 300w, https://daoker.cc/wp-content/uploads/2023/11/image-5-768x270.png 768w" sizes="auto, (max-width: 975px) 100vw, 975px" /></figure>



<p>在OpenWRT命令行中一步一步执行下列命令：</p>



<pre class="wp-block-code"><code>mkdir -p /tmp/introot
mkdir -p /tmp/extroot
mount --bind / /tmp/introot
mount /dev/sda3 /tmp/extroot
tar -C /tmp/introot -cvf - . | tar -C /tmp/extroot -xf -
umount /tmp/introot
umount /tmp/extroot
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>参考文献</p>
<cite><a href="https://www.cnblogs.com/mokou/p/17061031.html">PVE部署Openwrt，并转换磁盘，Openwrt扩容 </a><br><a href="https://yooer.me/mkfs-ext4-error-ext2fs_create_orphan_file-symbol-not-found.html">mkfs.ext4 报错 ext2fs_create_orphan_file: symbol not found</a></cite></blockquote>



<p></p>



<p></p>



<h2 class="wp-block-heading">旁路由无法访问国内网络</h2>



<ul class="wp-block-list">
<li>1.LAN口取消桥接；</li>



<li>2.防火墙添加&#8221;iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE&#8221;，重启防火墙，立马就能访问了。</li>
</ul>



<h2 class="wp-block-heading">解决openwrt开机时要按enter才能启动</h2>



<figure class="wp-block-table"><table><tbody><tr><td>修改路由里的这个文件：/etc/inittab<br>文件里的 askfirst 改成 respawn<br><br>ttyS0::respawn:/usr/libexec/login.sh<br>hvc0::respawn:/usr/libexec/login.sh<br>tty1::respawn:/usr/libexec/login.sh</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">openwrt安装哪吒面板agent</h2>



<p>为了方便监控各个设备的情况，想到了能不能和监控vps的哪吒面板整合，想不到真的有：</p>



<p>下载哪吒面板监控端：<a href="https://github.com/naiba/nezha/releases" target="_blank" rel="noreferrer noopener">https://github.com/naiba/nezha/releases</a></p>



<pre class="wp-block-code"><code>wget https://github.com/naiba/nezha/releases/download/v0.14.4/nezha-agent_linux_amd64.zip</code></pre>



<p>解压并赋予权限</p>



<pre class="wp-block-code"><code>unzip nezha-agent_linux_amd64.zip
rm nezha-agent_linux_amd64.zip 
chmod +x /root/nezha-agent</code></pre>



<pre class="wp-block-code"><code>vim /etc/init.d/nezha-service</code></pre>



<pre class="wp-block-code"><code><strong>#!/bin/sh /etc/rc.common</strong>

START=99
USE_PROCD=1

start_service() {
 procd_open_instance
 procd_set_param command /root/nezha-agent -s 面板通信地址:端口 -p 秘钥 -d
 procd_set_param respawn
 procd_close_instance
}

stop_service() {
    killall nezha-agent
}

restart() {
 stop
 sleep 2
 start
}</code></pre>



<pre class="wp-block-code"><code>chmod +x /etc/init.d/nezha-service</code></pre>



<pre class="wp-block-code"><code>/etc/init.d/nezha-service enable &amp;&amp; /etc/init.d/nezha-service start</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="686" src="http://daoker.cc/wp-content/uploads/2022/11/image-8-1024x686.png" alt="" class="wp-image-617" srcset="https://daoker.cc/wp-content/uploads/2022/11/image-8-1024x686.png 1024w, https://daoker.cc/wp-content/uploads/2022/11/image-8-300x201.png 300w, https://daoker.cc/wp-content/uploads/2022/11/image-8-768x515.png 768w, https://daoker.cc/wp-content/uploads/2022/11/image-8.png 1282w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto398.html/feed</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<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 loading="lazy" 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="auto, (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 loading="lazy" 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="auto, (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 loading="lazy" 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="auto, (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/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>安装openwrt的树莓派根据CPU温度控制风扇</title>
		<link>https://daoker.cc/daokerto381.html</link>
					<comments>https://daoker.cc/daokerto381.html#comments</comments>
		
		<dc:creator><![CDATA[博主]]></dc:creator>
		<pubDate>Sat, 10 Sep 2022 03:37:37 +0000</pubDate>
				<category><![CDATA[搞机]]></category>
		<category><![CDATA[树莓派]]></category>
		<category><![CDATA[玩docker]]></category>
		<guid isPermaLink="false">https://daoker.cc/?p=381</guid>

					<description><![CDATA[由于没有支持pwm的风扇，所以没有实测： 引脚和接线图： 参考文献 https://blog.csdn.net [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>由于没有支持pwm的风扇，所以没有实测：</p>



<h2 class="wp-block-heading">引脚和接线图：</h2>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="600" height="672" src="http://daoker.cc/wp-content/uploads/2022/09/image-7.png" alt="" class="wp-image-382" srcset="https://daoker.cc/wp-content/uploads/2022/09/image-7.png 600w, https://daoker.cc/wp-content/uploads/2022/09/image-7-268x300.png 268w" sizes="auto, (max-width: 600px) 100vw, 600px" /></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>参考文献</p><cite>https://blog.csdn.net/luzze__123/article/details/118641526</cite></blockquote>



<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" style="flex-basis:100%">
<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="580" height="414" src="http://daoker.cc/wp-content/uploads/2022/09/image-10.png" alt="" class="wp-image-389" srcset="https://daoker.cc/wp-content/uploads/2022/09/image-10.png 580w, https://daoker.cc/wp-content/uploads/2022/09/image-10-300x214.png 300w" sizes="auto, (max-width: 580px) 100vw, 580px" /></figure>
</div>



<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="517" height="243" src="http://daoker.cc/wp-content/uploads/2022/09/image-9.png" alt="" class="wp-image-388" srcset="https://daoker.cc/wp-content/uploads/2022/09/image-9.png 517w, https://daoker.cc/wp-content/uploads/2022/09/image-9-300x141.png 300w" sizes="auto, (max-width: 517px) 100vw, 517px" /></figure>
</div>
</div>



<p>S8050三极管基极(b)串联1K电阻（可省略）接树莓派GPIO12号引脚（硬件pwm引脚）</p>



<p>风扇正极接树莓派GPIO4号引脚(5V)</p>



<p>风扇负极接三极管集电极（c）</p>



<p>三极管发射极（e）接GPIO6号引脚（地GND）</p>
</div>
</div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" src="http://daoker.cc/wp-content/uploads/2022/09/IMG20220910144537-768x1024.jpg" alt="" class="wp-image-393" srcset="https://daoker.cc/wp-content/uploads/2022/09/IMG20220910144537-768x1024.jpg 768w, https://daoker.cc/wp-content/uploads/2022/09/IMG20220910144537-225x300.jpg 225w, https://daoker.cc/wp-content/uploads/2022/09/IMG20220910144537-1152x1536.jpg 1152w, https://daoker.cc/wp-content/uploads/2022/09/IMG20220910144537-1536x2048.jpg 1536w, https://daoker.cc/wp-content/uploads/2022/09/IMG20220910144537-scaled.jpg 1920w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>



<h2 class="wp-block-heading">Python版：</h2>



<h3 class="wp-block-heading">pwm案例1：</h3>



<pre class="wp-block-code"><code># -*- coding: utf-8 -*-
 
import RPi.GPIO as GPIO
import time
 
# 控制风扇的GPIO
FAN_GPIO = 11
# 低温阈值，低于它则关闭风扇
MIN_TEMP = 35
# 高温阈值，高于它则全速运转
MAX_TEMP = 55
# 多长时间读取一次CPU温度，单位秒
SAMPLING = 30
 
 
# 单位为千分之一度
def get_cpu_temp():
    with open('/sys/class/thermal/thermal_zone0/temp') as f:
        cpu_temp = int(f.read())
    return cpu_temp
 
 
def main():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(FAN_GPIO, GPIO.OUT)
 
    # 初始化PWM的频率，frequency=50Hz
    pwm = GPIO.PWM(FAN_GPIO, 50)
    # 为防止风扇卡死，开机全速运行n秒
    pwm.start(100)
    time.sleep(5)
    pwm.stop()
 
    try:
        while 1:
            temp = get_cpu_temp()
            print('CPU temperature:', temp)
            if temp &lt; MIN_TEMP * 1000:
                # 低于低温阈值，则关闭风扇
                print('fan to stop running')
                pwm.stop()
            elif temp &gt; MAX_TEMP * 1000:
                # 超过高温阈值，则全速运行
                print('full-speed operation')
                pwm.start(100)
            else:
                # 在低温阈值和高温阈值之间时，则根据占空比使用PWM控制风扇转速。
                dc = (temp - MIN_TEMP * 1000) * 100 / ((MAX_TEMP - MIN_TEMP) * 1000)
                pwm.start(dc)
                print('PWM duty cycle:', dc)
            # 设置采样频率
            time.sleep(SAMPLING)
    except KeyboardInterrupt:
        pass
 
    pwm.stop()
    GPIO.cleanup()
 
 
if __name__ == '__main__':
    main()</code></pre>



<h3 class="wp-block-heading">pwm案例2</h3>



<pre class="wp-block-code"><code>import RPi.GPIO as gpio
import time
fan_gpio_pin = 12
temp_max = 500
temp_on = 450
temp_min = 400
def get_cpu_temp():
    with open('/sys/class/thermal/thermal_zone0/temp') as f:
        cpu_temp = int(f.read())
    return cpu_temp/100

def main():
    gpio.setwarnings(False)
    gpio.setmode(gpio.BOARD)
    gpio.setup(fan_gpio_pin, gpio.OUT, initial=gpio.LOW)
    pwm = gpio.PWM(fan_gpio_pin, 50)
    hasFanStarted = False
    while True:
        temp = get_cpu_temp()
        # print(temp)
        if temp &lt; temp_min:
            if hasFanStarted:
                pwm.start(0)
                hasFanStarted = False
        elif temp &gt;= temp_on and temp &lt;= temp_max:
            pwm.start(temp-temp_on+50)
            hasFanStarted = True

        elif temp &gt; temp_max:
            pwm.start(100)
            hasFanStarted = True
        time.sleep(1)

if __name__ == '__main__':
    main() </code></pre>



<h3 class="wp-block-heading">开关控制：</h3>



<pre class="wp-block-code"><code>import RPi.GPIO as gpio
from time import sleep

Temper_HI = 47  # 风扇启动温度
Temper_LO = 40  # 风扇关闭温度
gpio_pin = 12
# 初始化GPIO针脚控制
gpio.setmode(gpio.BOARD)
gpio.setup(gpio_pin, gpio.OUT)

while True:
    with open("/sys/class/thermal/thermal_zone0/temp", "r") as f:
        temper = int(f.read()) // 100 / 10  # 计算温度，保留一位小数
    # print(temper)
    if temper &gt; Temper_HI:
        gpio.output(gpio_pin, gpio.HIGH)

    if temper &lt; Temper_LO:
        gpio.output(gpio_pin, gpio.LOW)
    sleep(1) </code></pre>



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



<h3 class="wp-block-heading">案例1：</h3>



<pre class="wp-block-code"><code>#! /bin/bash
fan_is_on=0
if &#91; ! -e "/sys/class/gpio/gpio18/value" ]
    then
    echo "export gpio 18"
    echo "18" &gt; "/sys/class/gpio/export"
fi

echo "out" &gt; "/sys/class/gpio/gpio18/direction"

while ((1))
do
TEMP=$(cat /sys/class/thermal/thermal_zone0/temp)
echo $TEMP
if &#91;&#91; $fan_is_on -eq 1 ]] &amp;&amp; &#91;&#91; $TEMP -lt 40000 ]]
    then
    fan_is_on=0
    echo "fan off"
    echo "0" &gt; "/sys/class/gpio/gpio18/value"
elif &#91;&#91; $fan_is_on -eq 0 ]] &amp;&amp; &#91;&#91; $TEMP -gt 45000 ]]
    then
    fan_is_on=1
    echo "fan on"
    echo "1" &gt; "/sys/class/gpio/gpio18/value"
fi
sleep 1s
done </code></pre>



<h3 class="wp-block-heading">案例2</h3>



<pre class="wp-block-code"><code>#!/bin/bash
 echo 14 &gt; /sys/class/gpio/export
 echo out &gt; /sys/class/gpio/gpio14/direction
 echo 1 &gt; /sys/class/gpio/gpio14/value
 sleep 2
 while true
 do
     cputemp=`cat /sys/class/thermal/thermal_zone0/temp`
     valve=70000
     temp=`expr $cputemp`
     echo $temp
     if (( $temp &gt;= $valve ))
     then
         echo 1 &gt; /sys/class/gpio/gpio14/value &amp;
     else
         echo 0 &gt; /sys/class/gpio/gpio14/value &amp;
     fi
     sleep 60
 done</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://daoker.cc/daokerto381.html/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
