big_uncle

Results 105 issues of big_uncle

前段时间稚晖君做出了一个[迷你机械臂](https://www.bilibili.com/video/BV12341117rG),非常强大,里面使用的示教器[Peak](https://github.com/peng-zhihui/Peak)的软件部分借鉴的是另一个开源项目[X-TRACK](https://github.com/FASTSHIFT/X-TRACK),在好奇心的驱使下我去大致阅读了两份代码,除却LVGL图形设计部分外,整个工程的框架设计对我有写额外的启发。 稚晖君自己也对X-TRACK写过一个分析文档,他绘制的代码结构如下图所示。 ![img](https://camo.githubusercontent.com/5136d7f564513f094d512211dd2128a803b4dfc91646c12457ae1da51e91a9de/68747470733a2f2f70656e677a68696875692d6d61726b646f776e2e6f73732d636e2d7368616e676861692e616c6979756e63732e636f6d2f696d672f32303231303732323136353735382e706e67) 其中的**消息框架**和**HAL层**对我有些启发。 # 消息框架 消息框架是一个消息发布订阅机制,类似于ROS里面的话题功能,这个机制可以将各个模块相互独立起来,而且对于一对多的消息传递这种情况,消息框架是非常容易管理的。 因为源代码中使用C++编写消息框架,而单片机开发用的更多的还是C语言,于是我自己简单写了一个C语言版的,其中动态数组`cvector`的实现修改自[这篇博客](https://blog.csdn.net/dengxu11/article/details/7311820)。 * cvector.h ```c #ifndef CVECTOR_H #define CVECTOR_H # include # include # include # define MIN_LEN 5 # define EXPANED_VAL 1 struct...

RM
STM32

前段时间尝试使用陀螺仪作为机器人云台的位置环反馈值,在底盘跟随的大前提下,这样做是更符合第一视角的操作的。 我去年比赛的时候买了几个维特智能的型号为WT901的九轴,当时时间紧没有用上,结果不用不知道,一用吓一跳,陀螺仪延迟高的吓人,使用J-Scope看到的图像如下: ![image](https://user-images.githubusercontent.com/35989223/54519278-1ad15700-49a1-11e9-9007-17d4b6983d16.png) 从图中可以看出延迟大约有200ms,这种延时已经不再具备方位参考值了。 我在仓库里东翻西找,找到了两个三十多块的MPU9250,相比于我买的一百来块的陀螺仪来说便宜了很多,于是我开始尝试手动解析数据。 # 读取原始数据 MPU9250内部其实是由6轴陀螺仪**MPU6500**和磁力计**AK8963**组成,可以测量三轴加速度,三轴角速度和三轴磁场强度,此外还可以测量温度,但这里我没有用到温度的数据。 数据解析的第一步就是读取原始数据。MPU6500支持IIC通信和SPI通信,AK8963只支持IIC通信,但可以通过MPU9250为中介使用SPI通信,只是比较麻烦,所以这里我选择了IIC通信。 ## IIC地址 MPU9250的IIC地址取决于AD0口的电平高低,如果AD0接GND,则地址为0x68,如果接VCC,则地址为0x69,。 这里说一下一个坑: > 在读取时,注意需要人工将地址左移1位(I2C读写为左对齐,第8位要存读写标志位) 最开始我不知道这个,一直把地址写成0x68,结果死活读不到数据,后来才发现要写成0xD0。 MPU9250的其他地址可以查看它的寄存器数据手册,我这里列出了我用到的一部分寄存器: ```c #define MPU9250_ADDRESS 0xD0 //AD0接GND时地址为0x68,接VCC时地址为0x69 #define MPU_PWR_MGMT1_REG 0X6B //电源管理寄存器1 #define MPU_GYRO_CFG_REG 0X1B //陀螺仪配置寄存器...

RM

圆筒镜艺术是一门比较冷面的艺术,百度里搜索也找不到几张图片,不过大家一看就懂,比如下面这两幅图: ![image](https://user-images.githubusercontent.com/35989223/93284444-4f77d600-f805-11ea-8a1e-6e52d6da0af4.png) ![image](https://user-images.githubusercontent.com/35989223/93284470-61597900-f805-11ea-9548-9e1f7fda867f.png) 在我看来这种艺术的精妙之处在于得到的作品看似畸变无序,看不出是个什么玩意儿,但当把圆筒镜放上去的时候,相信所有人都会惊呼【卧槽】 然而我虽然热爱绘画,但我的绘画水平不行,所以我决定用代码的方式来绘制。 # 原理分析 直观来看,圆筒镜成像的过程如下所示: ![image](https://user-images.githubusercontent.com/35989223/93284819-0f652300-f806-11ea-9b2c-14793351528e.png) 但是这个没有揭示本质,实际的成像过程应该是下图所示: ![image](https://user-images.githubusercontent.com/35989223/93285384-4ab42180-f807-11ea-8ef5-1751686967a4.png) 可以把成像过程看为两步。图中O点为相机的聚焦点,①为相机的像平面,即我们期望在相机里看到的画面,②为①的纸张平面的透射变换,③为②对于圆筒镜的反射成像,③也就是我们最后要得到的图像。 ## 透射变换 透射变换很简单,只需要求出纸张平面和相机像平面之间的单应矩阵即可,我这里直接使用相机标定中的棋盘,识别到角点后直接使用OpenCV的`findHomography`函数求得单应矩阵,这里用H表示,则两个坐标系之间的关系如下: ![image](https://user-images.githubusercontent.com/35989223/93285686-12f9a980-f808-11ea-91d2-5e352e8d4f19.png) ## 反射变换 反射变换则在纸张平面上分析,但因为圆筒镜的镜面是曲面,反射面法线方向都是改变的,所以我们首先要找到图像上每一点对应的法线方向,或者说对应的圆筒镜上的反射点。 如下图所示,我们可以根据相机像平面得到反射点,并且很容易知道,图像中同一列的像素点对应的反射点是一样的。 ![image](https://user-images.githubusercontent.com/35989223/93286057-d8444100-f808-11ea-8e89-43ec101c6594.png) 回到纸张平面,考虑一点(x, y),其反射点为(△x, -△y),反射成像点为(x', y') ![image](https://user-images.githubusercontent.com/35989223/93286382-b1d2d580-f809-11ea-8458-091a32fde352.png) 通过简单的平面几何分析可以得到反射成像点的公式。 ![image](https://user-images.githubusercontent.com/35989223/93286846-b1870a00-f80a-11ea-99ba-06b532060699.png) 写成矩阵形式即为: ![image](https://user-images.githubusercontent.com/35989223/93286919-e09d7b80-f80a-11ea-86a4-d51618a66546.png)...

CV

前段时间MIT的新型迷你猎豹机器人火遍全网,当时我就心里痒痒要做一个,但是正在备赛RM就没做,后来看到祁乐老哥做了一个舵机4足机器人([看这里](https://space.bilibili.com/23117381/)),更是刺激了我,现在终于比赛结束了(sad :cry:),准备动手研究四足机器人。 我也打算做一个带12个舵机的四足机器人,一是这种类型的四足比较像一个四足,是我最喜欢的一种形态,二是穷,买不起好电机。 这是一系列制作过程记录,也可以把它当做教程吧,不定期更新,我也不知道什么时候能做出来,毕竟现在一穷二白,电机都买不起,还正值期末考试。 那么正式开始了,首先是腿部算法。 --- # 腿部简介 首先还是看一看机器人的腿部结构吧,这里直接借用祁乐老哥的机器人一用 ![image](https://user-images.githubusercontent.com/35989223/59141331-74b04e80-89dd-11e9-872f-d55e09af2fbd.png) 从上图可以看出,每一个腿部有三个自由度,所以对应的我们每次更新机器人姿态时都要计算出这三个舵机的角度。 首先看看两个自由度下,腿部的在平面内运动的情况。 # 腿部平面运动 可以将模型抽象如下 ![image](https://user-images.githubusercontent.com/35989223/59141409-04a2c800-89df-11e9-8fa4-35d03a3818b4.png) 我们的目标转变为根据C点坐标计算α和β角度,这里其实就是解三角函数。上图中的直线a和b就是机器人的两条腿,是可以直接测量出来的。 首先用勾股定理求出直线c的长度,然后在△ABC中运用余弦定理,可计算出∠β和∠BAC,进而求出∠α 最后求出的结果如下 ![image](https://user-images.githubusercontent.com/35989223/59141506-a0810380-89e0-11e9-8ec8-8967ead5afc1.png) 上述公式中要注意的就是坐标系的正方向的选取,也就是x和y的正负号问题。 根据上述公式就可以把二维平面的坐标转换为两个舵机的角度,进而转换为舵机的控制信号。当然要还要注意C点的取值范围,毕竟腿长是有限的。 # 腿部三维运动 现在把腿放在三维空间中看看,可以把模型简化如下 ![image](https://user-images.githubusercontent.com/35989223/59141625-4e8dad00-89e3-11e9-9d26-8068a2250134.png) 上图中黑色的两条线代表腿部的大腿和小腿两段,α、β和γ分别代表腿部三个舵机的角度位置。 面ABCD是机器人直立时腿部所在平面,即面ABCD垂直于地面。 很容易看出,腿部一定在面ABDE中,那么∠α和∠β就可以在面ABDE中,在二维平面内,用上述的方法求解出。∠γ可以在△BCD中求解出来。 最后求出的结果如下:...

Project

一切都要追溯回硬件组组长建议我搞无线下载器的那天开始,掐指一算刚好一个月,就在今天,我终于搞定了两个ESP8266模块在不借助额外的WiFi的情况下实现相互通信,搞定这一步后做一个串口转WiFi透传模块那还不是轻轻松松。 # ESP8266简介 ESP8266这个词可以理解为ESP8266EX芯片(当然,后面也有可能出现了其他芯片,这里首推ESP8266EX)以及依赖ESP8266EX芯片开发的系列模组的简称。 ESP8266EX是由乐鑫信息科技公司(Espressif)出品的一款应用于物联网编程的WiFi芯片。从它被设计出来,就引起了物联网开发的热潮,获得了业界同行的肯定。芯片的具体相关介绍,笔者这里就不占用章节篇幅,请读者自行查看乐鑫官网详细介绍。 ESP8266系列模组是深圳安信可(Ai-thinker)公司开发的一系列基于乐鑫ESP8266EX的超低功耗的UART-WiFi模块的模组,可以方便地进行二次开发,接入云端服务,实现手机3/4G全球随时随地的控制,加速产品原型设计。 从厂商角度来看,乐鑫是ESP8266的芯片厂商,安信可是依赖ESP8266生产模组,在ESP8266芯片的基础上,完善了外围器件布局和优化天线,并提供一系列开发方案的厂家。 这款芯片的设计初衷是针对loT物联网的,它可以作为`station`连接WiFi访问其他设备,可以作为`soft access port`配置为一个WiFi热点让其他设备连接,可以作为web服务器,也可以作为客户端请求数据,什么智能家居,远程操控,它都能搞定。 最最最棒的是,它的成本非常便宜,批发价格不到10块,太值了吧! ## 特点 * 802.11 b/g/n * 内置Tensilica L106 超低功耗 32 位微型 MCU,主频支持 80 MHz 和160 MHz,支持 RTOS * 内置10...

RM
other

碎碎念:俩月没写文章了,五月初终于得以返校,然后两周狂肝毕业设计,还好最后顺利毕业。6.10答辩,6.17毕业典礼,然后就回家了。回家后这段时间一直在参加今年优必选依托WAIC大会举办的WALKER仿真挑战赛,控制WALKER机器人完成指定任务。其中一项任务如下图所示。 ![image](https://user-images.githubusercontent.com/35989223/87440769-ed43fe80-c624-11ea-8e9e-7d7424b74a3e.png) 最后拿了第四名(前三有奖金),哎,还是差一点点。 这篇文章就记录一下我在比赛期间研究的机械臂角度解算,摘录自技术报告的第二章。 --- Walker机器人为仿人型机器人,普通的工业机械臂只需6个自由度即可到达活动空间内的任何位置,而人类的手臂因各个关节有角度限制,需要新增一个大臂扭转的自由度,所以Walker机器人的仿人机械臂为7自由冗余机械臂,使用传统的D-H模型求逆解的话会得到无数个解,一般选择先给定多出来的大臂扭转关节的角度,再求解其余6个自由度。 本队采用几何法求解机械臂的关节角度,遍历了大臂扭转关节的所有角度值,分别求解出对应的其余关节的角度值,然后设计代价函数,选取与上一个状态的角度相差最小的解作为当前状态的角度解。 此处以机器人左臂为例,对机械臂的逆运动学解算过程作简要介绍。左臂的自由度分布关系如图2-1所示。 ![image](https://user-images.githubusercontent.com/35989223/87441129-50ce2c00-c625-11ea-8887-7fa3cf576063.png) 为简化分析,可以将肩部的关节①②③合并为一个球关节,将腕部的关节⑤⑥⑦合并为一个球关节,最后简化图如图2-2所示。为方便解算,我们采用位姿分离的方法分析,即任务腕关节只影响末端手掌的姿态,肩部关节和肘部关节只影响腕部的空间坐标。首先根据腕部的空间坐标求解前四个关节角度。 机械臂大臂和小臂的任一姿态可由图2-3表示。因为肘部不能外拐,所以关节④的角度一定是一个负值,如图所示,在△OAB中可由余弦定理求得∠OAB,进而可求得关节4的角度。 ![image](https://user-images.githubusercontent.com/35989223/87441179-65aabf80-c625-11ea-8baf-379da8447fe2.png) 根据B点的坐标,可以求得角α和角β。如果关节③的角度为0,那么显然机械臂OAB只会在面OCB内运动,而随着关节③角度的变化,A点可以绕直线OB作圆周运动,设面OAB与面OCB的夹角为θ。将基坐标系Oxyz依次绕x轴、y轴旋转α和β角度,得到新的坐标系Ox’y’z’,如图2-4所示。在新的坐标系中,从面x’Oy’看A点,如图2-5所示,因此可以求得A点在坐标系Ox’y’z’中的坐标,然后再根据基坐标系和新的坐标系的转换关系即可求得A点在基坐标系中的坐标,进而求得关节①和关节②的角度。 ![image](https://user-images.githubusercontent.com/35989223/87441239-79562600-c625-11ea-83a6-802238f76279.png) A点在坐标系Ox’y’z’中的坐标(xA’, yA’, zA’)与在基坐标系Oxyz中的坐标(xA, yA, zA)的转换关系如下所示。 ![image](https://user-images.githubusercontent.com/35989223/87441295-870bab80-c625-11ea-8470-1e94e2c08a22.png) 针对关节③的角度求解,将基坐标系Oxyz依次绕x轴、y轴旋转关节①和关节②的角度,得到坐标系Ox’’y’’z’’,如图2-6所示。将AB投影至Ox’’y’’面,关节③的角度即为图中γ角。 ![image](https://user-images.githubusercontent.com/35989223/87441353-97238b00-c625-11ea-9f5e-f5b5cdd8d48c.png) 在求解到前4个关节的角度之后,即可求得AB杆在基坐标系中的方位(用旋转矩阵表示),而手掌的方位是已知量,即可求得手掌相对于AB的旋转矩阵。该旋转矩阵使用腕部的三个关节的角度w1、w2、w3表示如下。 ![image](https://user-images.githubusercontent.com/35989223/87441464-b3272c80-c625-11ea-89c9-6959caa7416f.png) 从上式可以看出,在求得旋转矩阵后,可以很方便地从中提取出三个关节的角度。至此,机械臂的七个自由度的角度均确定了。 我们以一定的步长遍历了面OAB与面OCB的夹角,求得了多组逆解,将这些解与上一个状态的角度相减,使用如下的代价函数评价角度的变化程度,选取所有逆解中代价函数最小的解作为当前状态的角度值,使得手臂在移动的过程中保持平滑。 ![image](https://user-images.githubusercontent.com/35989223/87441527-c2a67580-c625-11ea-9446-749a3e7e904f.png) 上式中|joints(i)|表示第i个关节的角度差值。

Project

今年的ICRA比赛的一大亮点是新增了哨岗,使用单目相机识别场地中的机器人,提供小地图。传统的目标检测算法都是预测出能框住目标的矩形框(如下图所示),但是用在机器人定位上不太合适,误差太大。 ![image](https://user-images.githubusercontent.com/35989223/81128690-75968b00-8f74-11ea-82ab-5e5cfd12b43a.png) > 截图来自[加州伯克利大学 Robomaster](https://www.bilibili.com/video/BV1ae411p7So) 真正符合需求的是目标的6D姿态检测,比较出名的就是`SSD-6D`和`YOLO-6D`(毕竟目标检测领域就它俩最快),但其实这个快是在有给力的GPU的前提下的,我试了一下用我的破电脑的CPU跑yolo-tiny,竟然要2S一帧,人家的GPU效果是145fps,差距未免太大。 ![image](https://user-images.githubusercontent.com/35989223/81129212-3832fd00-8f76-11ea-90b2-e55c35931148.png) 上图便是6D的目标检测效果,大致思路是物体中心的栅格负责检测物体的八个角点,但其实我的需求只需要检测地面的四个角点,所以我的模型还可以简化。 另外YOLO的网络太过于庞大,它不仅要检测目标,还要目标分类,支持上千种物体,我完全用不上,我只有一个类,所以我决定自己设计网络结构。 # 网络结构 我设计的网络结构如下: ![image](https://user-images.githubusercontent.com/35989223/81130375-1e93b480-8f7a-11ea-937e-a9c31b9c69a6.png) 采用的FCN全卷积神经网络结构,因此预测的时候支持任意分辨率输入。 训练时的输入我将缩放至416*416,外加有3个颜色通道,因此输入维度为3x416x416,层与层之间我加入了一个大小为3,步长为1,paddind为1的卷积层和一个大小为2,步长为2的卷积层,实现了卷积和下采样操作,最后得到一个48x6x6的输出。 最后一层是预测层,我删去了类别,同时增加了坐标输出,因为有4个角点,所以一共需要八个输出,另外还有一个置信度,表示的是当前栅格为底面中心点的概率,所以一共是九个维度的输出。 从上面的分析可以看出,我把图像分成了6x6的小格子,每个格子负责判断自己是否是底面中心,并预测出四个角点的位置,最后选取置信度较高的格子预测出的角点位置作为最终的角点位置。 需要注意的是,最后的网络层输出需要使用sigmoid函数将其输出限制在0-1之间,因此预测出的角点坐标和真实的图像坐标之间还有一个公式换算,我设计的换算公式如下图所示: ![image](https://user-images.githubusercontent.com/35989223/81131191-e772d280-8f7c-11ea-93a0-fcc7f3b493e6.png) 其中`img_scale`是卷积后图片缩小的比例,在我这里就是**416/6**。 # 损失函数 损失函数分为两部分,首先要根据图片的标签得出哪个栅格是底盘的中心。如果栅格不是底盘的中心,那么该栅格的损失函数为置信度的平方,既需要使其置信度越低越好,如果是底盘的中心,那么该栅格的损失函数为(1-置信度)^2和四个预测角点与真实角点的距离的平方和,既要使该栅格的置信度越接近1越好,角点越接近真实值越好。 公式如下: ![image](https://user-images.githubusercontent.com/35989223/81131379-93b4b900-8f7d-11ea-8a25-2be9755abe7a.png) 这里为了让损失函数快速收敛,我在每一层网络中还加入了了BatchNorm层,使输出正则化。 # 代码实现 我使用`pytorch`来搭建网络,之前想使用百度的paddle paddle(飞浆),毕竟有免费GPU白嫖,然而捣鼓一番发现不好用,特别是自定义损失函数很不好用,又回到pytorch才发现,真香!...

AI
CV

在一些特定的应用场景中,需要把一个窗口置顶,置顶的窗口可以在其他窗口激活的情况下依然保持在最上面。 先说几个概念: **窗口**:Windows是以窗口作为主要交互界面的系统,我们把那个能拖来拖去,一般带有最大化最小化关闭按钮的大方框叫做窗口,但是其实窗口的定义可以更广泛一点,一个按钮是窗口,一个文本框也是窗口。 **父窗口**:如果一个按钮(Button)被放在一个Panel(面板)上, 那按钮的父窗口就是Pane, 同样Panel的父窗口是Form,Form就是我们平时说的窗口了。 **顶级窗口**:Form的父窗口是什么? 有人说是桌面(Desktop),也有人说没有父窗口,但是通过实验得知,它没有父窗口,没有父窗口的窗口我们叫做顶级窗口,我们平时说的窗口,都是指的顶级窗口。 **句柄**:Handle,句柄是一种特殊的指针,指向的是内存里的对象, 通俗得讲它就是一个窗口(事实上远不止窗口)的把手,你有了这个句柄就能开窗关窗,改变窗口的状态。 只要我们获得顶级窗口的句柄,改变它的状态,把"不置顶"改为"置顶",就达成目的了。 # 实现逻辑 只需要不停地找窗口的父窗口, 直到某个窗口的父窗口的句柄为0(不存在),那就找到了该窗口的顶级窗口。 有函数`GetParent()`,能取得窗口的父窗口。 取得顶级窗口的句柄后, 有函数`SetWindowPos`, 指定参数HWND, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_DRAWFRAME | SWP_NOSIZE 就能置顶一个窗口。...

Win32

今天遇到一个小需求,要将一个网页中的图片都下载下来,有一百多个图片,手动右键另存为不太现实,是时候使用脚本了。 按`F12`打开调试窗口,复制输入以下代码: ```javascript //一个对象,存储页面图片数量和下载的数量 var monitorObj = { imgTotal: 0, imgLoaded: 0 } //创建a标签,赋予图片对象相关属性,并插入body var createA = function (obj, index) { var a = document.createElement("a"); a.id = obj.id; a.target =...

web

闲着无聊学一学python爬虫,定义了一个获取页面HTML内容的函数: ```python def askURL(url): request = urllib.request.Request(url)#发送请求 try: response = urllib.request.urlopen(request)#取得响应 html= response.read()#获取网页内容 # print (html) return html except urllib.error.URLError as e: if hasattr(e,"code"): print (e.code) if hasattr(e,"reason"): print (e.reason)...

web