@choury 你暴露了你的密码算法,我感觉能破解出来了:-D
ROS Group 产品服务
Product Service 开源代码库
Github 官网
Official website 技术交流
Technological exchanges 激光雷达
LIDAR ROS教程
ROS Tourials 深度学习
Deep Learning 机器视觉
Computer Vision
Posts made by weijiz
-
RE: 一种新的类型的密码管理软件Lesspass
@choury 给自己加个域名,或者起个容易记住的名字就是了。总是会有work around。要是你自己算也是会有这个问题的。这个就相当于把你那个过程自动化了。
-
秋山图
“……黄大痴,哎,您看过大痴的《秋山图》吗?”
一个秋夜,王石谷到颐香阁作客,同主人浑南田,一边喝茶,一边谈话。
“呵,没有见过,您见过吗?”
大痴老人黄公望,同梅道人,黄鹤山樵,都是元代绘画的神手。浑南田一边说,一边想起曾经见过的《沙碛图》、《富春卷》,仿佛还在眼前一般。
“是啊,可以说见过,也可以说没有见过,这是一件怪事哩……”
“那到底见过还是没有见过呢?”
浑南田惊异地瞅着王石谷的脸,问道:
“见过的是摹本吗?”
“不,也不是摹本,算是见过了真迹……不过,不但我,烟客先生(王时敏)和廉州先生(王鉴)对这《秋山图》也都有过一段因缘。”
“您要是有兴趣,我就谈一谈!”
“请吧!”
浑南田拨拨灯檠的火头,便请客人谈谈这件事。
是元宰先生(董其昌)在世的时候,有一年秋天,正同烟客翁谈画,忽然问翁,见过黄一峰的《秋山图》没有。您知道翁在画道上是师法大痴的,凡是大痴的画,只要留在世上的,差不多全见过,可是这《秋山图》却始终没有见过。
“不,不但没有见过,连听也几乎没有听说过。”
烟客翁这样回答了,觉得挺不好意思。
“那么,有机会务必看一看吧。那画比《夏山图》、《浮岚图》更出色哩。大概可以算大痴老人生平所作中的极品了。”
“有这样好的作品,一定得看一看,这画在谁手里呢?”
“在润州张氏家,您去金山寺的时候,可以去登门拜访,我给您写封介绍信。”
烟客翁得了元宰先生的介绍信,马上出发到润州去。他想,张氏家既收藏这样的好画,一定还有许多历代妙品……因此他在自己西园的书房里呆不住了。
可是到润州一访问,一心想往的张氏家,虽然屋院很大,却显得一片荒凉。墙上爬满了藤蔓,院子里长着长草,成群的鸡鸭,见到客来表示好奇的神气。翁对元宰先生的话都怀疑起来了:这种人家能收藏大痴的名画吗?但既已来了,也不能过门不入。对门口出来接待的小厮,说明了来意,是远道而来,想拜观黄一峰的《秋山图》的,然后,交出了思白先生的介绍信。
不一会儿,烟客翁被请到厅堂里。这儿空空洞洞的,陈设着紫檀木的椅子,上面蒙着一层淡淡的尘土。……青砖地上,飘起一股荒落的气味。幸而那位出来接待的主人,虽然一脸病容,却还风雅,苍白的脸色,纤巧的手势,有贵族的品格。翁和主人作了初见的应对之后,马上提出想拜观黄一峰名画的愿望。翁好像有些迷信的想法,以为现在不马上观看,这画便会烟消云散了。
主人立刻答应。原来这厅堂正墙上,就挂着一幅中堂。
“这就是您要看的《秋山图》。”
烟客翁抬头一看,不觉发出一声惊叹。
“画是青绿山水,蜿蜒的溪流,点缀着小桥茅舍……后面,在主峰的中腰,流动着一片悠然的秋云,用蛤粉染出浓浓淡淡的层次。用点墨描出高高低低的丛山,显出新雨后的翠黛,又着上一点点朱笔,到处表现出林丛的红叶,美得简直无法用言语来形容了。好一幅绚烂的图画,而布局又极为宏大,笔致十分浑厚……在灿烂的色彩中,自然地洋溢着空灵淡荡的古趣。”
烟客翁完全被迷住了,恋恋不舍地看着看着,真是愈看愈觉神奇。
“怎样,喜欢吗?”
主人笑眯眯地望着翁的侧脸。
“神品,神品,元宰先生的称赏果非虚言,耳闻不如目见,以前我所见过的许多佳作,对此都要甘拜下风了。”
烟客翁一边说,一边眼睛仍没离开画幅。
“是么,真是这样的杰作吗?”
翁听了这话,不觉把吃惊的眼光转向主人。
“什么,您觉得我看得不对吗?”
“不,没有什么不对,实际是……”
主人像少女似的羞红了脸,然后淡淡一笑,怯生生地看着墙上的画,接下去说:
“实际是,我每次看这画时,总觉得好像在睁眼做梦。不错,《秋山图》是美的,但这个美,是否只有我觉得美呢?让别人看时,也许认为只是一张平常的画。不知为什么,我总是这样怀疑。这也许是我的迷惑,也许在世上所有的画中,这幅画是太美了,其中必有一个原因。反正我就一直那么感觉,今天听了您的称赏,我才安心了。”这时烟客翁对主人的辩解,也没特别放在心上,这不仅是因为他看画看入迷了,同时也认为这主人不懂得鉴赏,硬充内行,所以胡乱说出这种话来。
过了一会儿之后,翁告别了这个荒院一般的张氏家。
可是总忘不了那幅留在眼里的《秋山图》。对于师事大痴法灯的烟客翁,什么都可以放弃不要,只一心想得到这幅《秋山图》。翁是一位收藏家,在家藏书画中,甚至用二十镒黄金易得的李营丘《山阴泛雪图》,比之这幅《秋山图》的神趣,也不免相形见绌。因之,以收藏家出名的翁,无论如何想得到这幅稀世的黄一峰的画。
于是,在逗留润州时,他几次派人到张氏家去交涉,希望把《秋山图》让给他,可是张氏家无论如何不肯接受翁的请求。据派去的人说,那位脸色苍白的主人说:“王先生既然喜欢这幅画,可以借给他,但是不能出让。”这使高傲的翁有点生气了。他想,现在不借,总有一天可以搞到手的,终于没有去借,就离开了润州。
以后过了一年,烟客翁又到润州,再次访问张氏家。那墙上的藤蔓和院中的荒草,仍如过去,可是出来应客的小厮,却说主人不在家。翁告诉他不见主人也行,只要再看看那幅《秋山图》就可以了。可是提了几次,小厮总推托主人不在,不让他进去,最后甚至把大门关上,不理睬了。于是,翁无可奈何,只好想象着藏在这荒院中的名画,怅然而归。
可是后来又见到元宰先生,先生对翁说,张氏家不仅有大痴的《秋山图》,还收藏着沈南田的《雨夜止宿图》,《自寿图》那样的名画。“上次忘记告诉了,这两幅跟《秋山图》一样,可称为画苑的奇观,我再给您封介绍信,务必去看看。”
烟客马上又派急使到张氏家,使者除了元宰先生的介绍信,还带去收购名画的现金。可张氏家仍同上次一样,别的画都可以,不过黄一峰那一幅是决不出让的。于是,翁也只好从此断念了。王石谷讲到此处,停了一下,又说:
“这是我从烟客先生那里听说的。”
“那么,只有烟客先生见过《秋山图》的了。”
恽南田捋捋长髯,点点头,眼望着王石谷。
“先生说是见到了,可到底是不是真见到,那就谁也说不上了。”
“不是您刚才还说……”
“嗨,您听我讲,等我讲完,您也会同我一样想了。”
这回,王石谷没喝茶,又娓娓地讲下去了。
烟客翁同我讲这事,是在第一次见过《秋山图》以后,经过快五十年星霜的时候,那时元宰先生早已物故,张氏家也不知不觉到了第三代。所以这《秋山图》已落谁家,是不是已经消灭了,也已无人知道。烟客翁好像如在手中似的给我讲了《秋山图》的妙处以后,又遗憾地说:
“这黄一峰的《秋山图》,正如公孙大娘的剑器,有笔墨而不见笔墨,只是一股难言的神韵,直逼观者的心头……正是神龙驾雾,既不见剑,也不见人。”
此后过了约一月,正是春气萌动时节,我独自去南方游历。翁对我说:“这是一个良机,务请探问《秋山图》下落,倘能再度出世,真画苑大庆了。”
我当也如此愿望,马上请翁写了介绍信,预定的旅程要到不少地方,一时不容易去访问润州张氏,我藏着介绍信一直到布谷啼叫时,还没有去找《秋山图》。
其间偶然听到传言,说那《秋山图》已落入贵戚王氏之手。在我旅程上烟客给的介绍信中,也有认识王氏的人。王氏既为贵戚,大概事先必定知道《秋山图》在张氏家。据书画界说,张家子孙接到王氏的使者,立地将传家的彝鼎、法书、连同大痴的《秋山图》,全都献给了王氏。王氏大喜,即请张家子孙坐上首席,献出家中歌姬,奏乐设筵,举行盛大宴会,以千金为礼。我听到这消息十分高兴,想不到饱经五十年沧桑之后,这《秋山图》竟然平安无恙,而且到了相识的王氏家。烟客翁多年来费了多少苦心,只想重见此画,鬼使神差,总以失败告终。现在王氏家不费我们的烦劳,自然地将此画如海市蜃楼般展现在我们眼前,正是天缘巧合。我便行李也不带,急忙到金阊王氏府,去拜观《秋山图》了。现在还记得很清楚,这正是王氏庭院的牡丹花在玉栏边盛放的初夏的午后。在匆匆谒见中,不觉就笑了起来:
“闻说《秋山图》今已归府上所有,烟客先生为此画曾大费苦心,现在他可以安心了,这样一想,真是十分快慰。”
王氏满脸得意地说:
“今天烟客先生、廉州先生都约好了要来,先到的请先看吧!”
王氏马上叫人在厅堂侧墙上挂起了《秋山图》。临水的红叶村舍,笼罩山谷的白云,远远近近侧立屏风似的青翠的群峰——立刻,在我的眼前,出现了大痴老人手创的比天地更灵巧的一座小天地。我带着心头的激动,眼睛一眨不眨地注视墙上的画。
云烟丘壑的气势,显然无疑是黄一峰的真品,用这样多的皱点,而墨色又这样灵活……着这样重叠的色彩,而看不出一点笔痕,除了痴翁,别人究竟是不可能的。可是——可是这《秋山图》,和烟客翁曾在张氏家所见那幅,确不是同一黄一峰的手笔。比之那幅,这恐怕是比较下品的黄一峰了。
王氏和合座的食客,都在我身边窥探我的脸色,我必须竭力不使失望之色露出脸上。尽管我十分注意,可是不服气的表情,还是不知不觉透露出来。过了一会儿,王氏带着担心的神气向我问了:
“您看如何?”
我连忙回答:
“神品,神品,难怪烟客先生大为惊奇。”
王氏的脸色,这才缓和起来,可是眉头眼底,好像对我的赞赏还有点不大满足。
这时候,恰巧对我大讲《秋山图》妙趣的烟客先生也到来了。翁同王氏寒暄着,显出高兴的笑容。
“五十年前在张家荒园看的《秋山图》,现在,又在华贵的尊府再度相逢,真是意外的因缘。”
烟客翁如此说着,举头观看墙上的大痴。这《秋山图》究竟是否翁见过的那幅,翁当然是最明白的。因此我也同王氏一样,深深注意翁看图的表情。果然,翁的脸上渐渐笼上了一道阴云。
沉默了一会儿之后,王氏更加不安了,他怯生生地问翁:
“您看如何,刚才石谷先生也大大赞赏了……”
我担心正直的翁,会老实回答王氏,心里感到一阵阵寒意。可是,大概翁也不忍使王氏失望吧,他看完了画,便郑重对王氏说:
“您得到这画,真是莫大幸运,它给府上的珍藏,又添加了一重光彩。”
可王氏听了,脸上的愁雾却更深了。
那时候,倘使那位迟到的廉州先生不突然到来,我们就会更加尴尬了,正当烟客翁迟迟疑疑不知如何赞赏时,幸而他来了,给座中增添了生气。
“这就是所谓《秋山图》吗?”
先生随意打座中招呼了一下,就去看黄一峰的画,看着看着,只是默默地咬嚼口边的胡子。“烟客先生,听说您五十年前见过这画呀?”
王氏愈加尴尬起来,又添上了这句话。廉州先生还没听翁说过《秋山图》的妙处。
“依您的鉴定,如何呢?”
先生吐了一口气,还照样在看画。
“请不客气地说吧……”
王氏勉强一笑,又向先生催问了。
“这个吗?这个……”
廉州先生又把嘴闭住了。
“这个?”
“这是痴翁第一名作……请看,这云烟的浓淡,多么泼辣的气概;这林木的色彩,正可说天造地设。那儿不是一座远峰么,从整个布局中,多么生动的气韵呀。”
一直没开口的廉州先生,对王氏—一指出画的佳处,开始大大赞赏了一番。王氏听了,脸色渐渐开朗,那是不消说了。这期间,我向烟客做了一个眼色,小声地说:
“这就是那幅《秋山图》吗?”
烟客翁摇摇头,回我一个奇妙的眼色:
“真是一切如在梦中,也许那张氏家的主人是一位狐仙吧?”
“《秋山图》的故事就是如此。”王石谷讲完了话,慢慢地喝了一杯茶。
“果然,真是一个怪谈。”
恽南田两眼盯视着铜檠的火焰。“以后王氏又热心地提了不少问题。归根到底,所谓痴翁的《秋山图》,除此以外,连张氏家的子孙也不知道了。过去烟客先生见过的那幅,要不是已隐灭不见,那就是先生记错了,我不明白究竟是怎么一回事,总不至全部是一场幻梦吧……”
“可是烟客先生心中,不是明明留下了那幅奇怪的《秋山图》,而且你心中也……”
“青绿的山岩,深朱的红叶,即使现在,还好像历历在目呢。”
“那么,没有《秋山图》,也大可不必遗憾了吧?”恽王两大家谈到这儿,不禁抚掌一笑。
-
一种新的类型的密码管理软件Lesspass
今天看到了一个密码管理软件,这个软件和其他这种类型的软件思路完全不一样。解决了以前这类软件的许多问题。下面就介绍一下它Lesspass.以下翻译自作者的博客。
管理自己的密码不是一个简单的问题。你或许需要一个密码管理软件来帮助你。这个系统很简单。首先管理软件生成随机的密码,然后软件把密码保存在一个加密的文件里。
整个系统也是很稳定的。你只要记住自己的主密码就可以了。现在对于每一个网站你都有一个唯一的密码。
我以前一直使用着这样的软件。但是我经常会遇到下面的问题:
- 怎么在不同的设备间同步密码文件
- 我在没有安装软件的电脑上如何使用我的密码。比如我父母的电脑
- 我怎么在手机上使用我的密码,如果手机上没有安装密码管理软件
然后我找到了一个简单的方法能够解决上面的所有问题。所以我编写了LessPass。
我想要一个开源的密码管理软件,而且不需要同步密码文件。
这个技巧在于计算出密码而不是生成和存储随机的密码。
LessPass根据网址,邮箱和其他你知道的信息去生成唯一的密码。
LessPass和其他你能在网上找到的密码管理软件完全不同,因为
- 你的密码不需要保存在服务器中
- 它也不需要你在不同的设备间同步密码
- 它是开源的
整个系统利用一个纯函数(也就是相同的输入总是给出相同的结果)。对于每一个登录,主密码,网站和选项,它就会返回一个唯一的密码。
根本不需要把密码保存在加密的文件中。你只需要利用工具重新计算一下密码就可以了。
以上就是基本的原理。至于具体的使用作者的博客上还有详细的介绍。这里就不说了。博客地址在这里
最后来一个软件的截图吧
-
一个简单的服务监控程序
一般来说服务器都会运行很多的服务,而通常会有各种各样的原因导致服务会挂掉。及时知道自己的服务是不是正常运行就是一个很必要的事情。现在网上也有免费的这样的服务。基本上就是由对方的服务器定期的请求你的相关服务。如果能正常请求则证明服务是没有问题的。我也一直用着这样的服务监控宝.
但是这种功能对于局域网内的服务就没办法进行监控了,因为不对外网开放,所以他们的服务器没办法请求到对应的服务。为了解决这个问题我就简单开发了一个服务监控程序。从想法到开发测试完成总共花了两个小时。所以说是个非常简单的程序。
这个程序要做哪些事情呢?首先是定期的请求你的服务。其次当服务状态发生变化的时候发送提示邮件到指定的邮箱。程序也没什么特别值得说的地方。不过这个功能还是非常好用的。我用CSharp开发,用mono编译可以跨平台执行。从运行效率,消耗资源上来看也是不错的。这里还是要夸一夸CSharp,感觉用其他语言开发的话就没有这么简洁和高效。项目地址在这里
下面show一下效果,个人感觉还是不错的
当服务挂掉的时候,收到的邮件是
当服务重新恢复的时候,收到的邮件是这样的
配合上手机的邮件客户端就更好用了。 -
小强机器人用户手册和教程目录
一. 开始使用
二. 小强ROS机器人教程
- 1. 基础操作介绍
- 2. 蓝鲸智能开源软件仓库的使用和ROS开机启动任务的配置
- 3. 在rviz中显示小强机器人模型
- 4. 惯性导航自主移动测试
- 5. 小强遥控图传app安卓版
- 6. 小强遥控图传windows客户端
- 7. 使用ps3手柄控制小强
- 8. kinect1代 ROS驱动测试与安装
- 9. 使用rostopic控制kinect的俯仰角度
- 10. 使用kinect进行自主移动避障
- 11. kinect跟随包turtlebot_follower
- 12. ROS显示kinect2代的点云
- 13. rplidar二代激光雷达的使用和利用udev给小车增加串口设备
- 14. 在gmapping下使用激光雷达rplidar a2进行建图
- 15. AMCL导航测试
- 16. 大范围激光雷达slam与实时回路闭合测试
- 17. 利用ORB_SLAM2建立环境三维模型
- 18. 利用DSO_SLAM建立环境三维模型
- 19. NLlinepatrol_planner的简单使用
- 20. 获取小车视觉里程计并在rviz中显示小车轨迹
- 21. 获取usb摄像头30fps的1080p图像流及120fps的VGA分辨率图像流
- 22. 操作6自由度机械臂
- 23. ROS入门手册
- 24. 在小强上使用伽利略视觉导航系统
- 25. 在小强上使用语音识别和语音合成功能
- 26. 使用奥比中光astrapro进行自主移动避障
- 27. bw_auto_dock自动充电功能包的使用和实现原理
- 28.使用Intel RealSense D400系列深度摄像头进行自主移动避障
- 29.安装使用zed相机cpu版本ros驱动
- 30.ORB_SLAM2包的详细配置和深度使用
- 31.kinect深度图转2d雷达数据
- 32.使用realsense深度摄像头进行跟随
三. 维护
四. 其他
- Ubuntu设置静态IP
- 视觉导航路径编辑器使用教程
- 小强的远程协助功能
- 小强ROS机器人障碍物识别演示
- 视觉导航在履带车中的运用
- Google激光雷达slam算法Cartographer的安装及bag包demo测试
- 原装和国产ps3手柄ros驱动程序
- 升级软件包以支持小强图传遥控app
- 小强自检程序
附件
-
线程和协程
在写程序的时候为了提高执行效率经常会用到线程.把任务分配到不同的线程里面同时执行,这样即使其中一个被阻塞了,程序还是可以运行,程序运行效率得到了很大的提高.线程是一个经常用到的概念这里就不多说了.
但是随着大家使用的线程越来越多,线程的劣势就越来越明显了.最有名的就是c10k问题.c10k问题是在写服务器程序时遇到的.如何使一个服务器同时和10K个客户端保持网络连接?一般的做法是给每个连接开一个独立的线程,但是这样就会有上万个线程.大量的资源被浪费在线程的管理上.线程已经无法再胜任这个任务了.
在javascript中处理异步问题的方式给了我们很好的思路.javascript程序是单线程的,也就是某一时刻只能有一个程序在运行.但是javascript却可以很好的处理异步问题.下面以一个读取文件的例子来说明
var fs = require('fs'); //引入文件处理库 fs fs.readFile('/etc/passwd', (err, data) => { if (err) throw err; console.log(data); }); console.log("I'm not blocking");
readFile函数的第一个参数是文件路径, 第二个参数是回调函数.当文件读取完成或者失败的时候回调函数会被执行.
这样当程序运行到readFile的时候就不会被阻塞.也就是说在文件读取完成之前就会在终端中显示出I'm not blocking
这是怎么实现的呢?
这是因为javascript有一个叫做Event loop的线程,专门用来监控事件有没有触发,如果触发就调用相应的回调函数.比如说上面的readFile函数,实际上是相当于在Event Loop里面注册了一下.这就是协程的概念.用一个主线程去完成各种异步操作.看起来好像各自都有自己独立的线程一样.
具体怎么实现一个协程呢?我们可以用python从头开始搭建出来.
在python中有一种叫做generator的东西.比如
a = xrange(0,100000)
这里a就是一个generator.一般使用时会觉得a是一个list.但是不是这样的.a中的元素只有在被使用到的时候才会被生成.def test(): for i in range(0,10): yield i a = test() # a就是一个generator a.next() # 返回1 a.next() # 返回2 a.next() # 返回3 # 直到10 a.next() # 抛出 StopIteration 异常
这样a就可以用来表现协程的状态.在Event Loop之中不停的执行协程返回的generator直到这个generator返回StopIteration为止,再执行对应的回调函数.
class Defer: def __init__(self): self.waitFlag = True self.results = None def resolve(self, results): self.results = results self.waitFlag = False def get_status(self): while self.waitFlag: yield True def code1(): mDefer = Defer() # start a new thread, and do some time consuming works # when the work is complete mDefer.resolve(results) return mDefer def code2(): mDefer = Defer() # start a new thread, and do some time consuming works # when the work is complete mDefer.resolve(results) return mDefer eventList = [] eventList.append(code1()) eventList.append(code2()) while True: for event in eventList: try: event.get_status() except: results = event.results
个人感觉原理上就是如此了.
实际上python中协程的用法
为了方便使用,python把协程相关的东西做成了asyncio库.并且在python 3.5之中增加了await, async关键字(和C#学的)
一个用协程的Hello World例子
import asyncio eventloop = asyncio.new_event_loop() asyncio.set_event_loop(eventloop) async def helloWorld(): await asyncio.sleep(10) print("Hello World") eventloop.run_until_complete(helloWorld()) # 这一句是阻塞的,要等待目标任务完成才会执行下面的程序 print("I'm blocking")
更详细的关于python的协程信息可以看这里
不过个人感觉python的协程还是不是很自然.没有C#做的好.这个应该是python语言自身的限制造成的.
-
大脑的工作是线性的吗?Weber原理介绍
我们很容易的就能在日常生活中发现我们的大脑对于变化量更加敏感。比如你可以轻松的听出一首曲子的旋律但是如果只是放出其中的一个音调一般人都是无法判断出这个音调具体是哪一个。当手从凉水突然伸到热水的时候你会觉得很烫,但是同样温度的热水,如果泡的时间比较长感觉就没那么强烈了。这样的描述还只是停留在定性的阶段。如何定量的去研究这个问题呢?
为了定量的去研究这个问题,我们就可以提出这样的一个问题。究竟变化量达到多少的时候人才会有感觉呢?以音调变化为例。当声音从440hz变到880hz时人可以轻易的分辨出来。那么我们就降低变化量看人还能不能分辨出来。比如变到440hz到450hz。就这样一直降低到人无法音调之间的变化为止。这样我们就得到了440hz下人的最小声音分辨率。重复这个过程就可以得到不同声音频率下人的声音最小分辨率。这个结果非常有意思,最小分辨率和声音的频率是成正比的。也就是说在440hz下可能人能分辨4hz的区别,但是到了880hz可能这个最小分辨率就只有8hz了。
经过很多类似的实验,我们发现了一个规律。不只是人的听觉,视觉,人的很多感受的这个最小变化量和基准量都是线性关系。这个规律就被称作Weber原理。利用数学公式描述就是下面这样。
其中Delta I 是最小分辨量,I是当前的基准值,Kw就是一个比例系数。
下面是实验数据表格
所以说大脑的工作基本会被认为是线性的。
你说这有什么意义呢?意义非常的大。比如说你买东西。买个100块的东西你可能会在意十几块的差价。但是如果买个上万块的东西你反而不在意这十几块的差价了。然而同样的十几块给人的感觉就完全不同。这在原理在商品定价策略上经常会被用到。
而我更想知道的是为什么会是线性的,这个和大脑的结构是不是有关系呢?还是进化中逐渐学习到的比较好的控制方式。这些都是未知的,等待未来的研究发吧。
想要更详细的了解Weber原理可以看这篇文章
-
视觉导航第一次应用于生产环境了
最近接到了用户的一个机器人订单。用户需要做一个展览用机器人,放在他的展厅里面。整个展厅长大概有三十多米,用户展览的产品在展厅一侧依次排开。用户需要机器人在每个展览的产品处停下并介绍这个产品。目前这个用户的机器人采用的是循迹的方法,在地板上贴上黑色的路线,并在上面标明这时第几个产品。但是用户对这些黑线很不满意,觉得破坏了整个展厅的氛围。
这种情况就必须用到我们的视觉定位系统了。然而视觉我们的视觉定位系统尚未成熟,程序能否准确稳定的运行也没有把握。但是用户既然有这样的需求也就只能试试了。
上面的视频就是视觉导航的效果展示。可以看到视觉定位的精度很高,工作起来也非常稳定。程序设置小车按照和黑线一致的方向运行。无论人为如何干扰,小车都能回到原来的方向。这个是利用陀螺仪和编码器的惯性导航无法做到的。惯性导航一旦轮子发生打滑,定位就会产生无法修复的误差。
上面是一个范围更加广的测试视频。至此小强的视觉导航系统终于被用于生产环境了。
-
来退个火吧
模拟退火算法是一个非常好用且简单的算法。它的思路也非常简单,下面就介绍一下模拟退火算法。
什么是模拟退火算法呢?
先从这个算法要解决的问题说起。实际上所有的算法都是为了一个目的——从解空间中把解给找出来。最好的当然是找到全局最优解,但是有时候局域最优解也是可以接受的。现在以一个简单的函数求极值的问题作为例子。
如下图这样一个函数,我们要找到其中的最小值。
最直接的方法是遍历所有的点。找到其中的最小点。但是很多时候由于解空间太大我们根本没有办法去遍历。这时候就需要高效的算法了。
比较直接的一种算法就是从图上任意一点出发,向两边开始移动,如果新位置的值小于之前位置的值,那么就移动到新的位置。在新位置再重复这个过程,直到无论怎么移动都没有当前的位置的值小的点。可以想象一个小球在曲线上滚动,最终会滚到一个很低的位置。这样我们就不需要遍历所有的点了。但是很有可能我们停在了一个极小值,这个值并不是全局的最有解。上面这个算法就是贪心算法,也叫做爬坡算法。而模拟退火算法就是从这个算法中改出来的。
退火就是金属的冷却过程,刚开始温度比较高,原子运动比较剧烈,随着时间的推移,温度逐渐下降,原子运动也逐渐稳定。而模拟退火就是在模拟这个过程。整体的算法过程和贪心算法一样。但是移动的步长会随着时间在减小。刚开始移动比较剧烈,随着时间的推移每次的移动范围逐渐减小。这样我们就更大的可能性能落在全局的最优点上。而且搜索的速度也要快很多。
下面就是模拟退火算法在小强中的具体应用。
小强的摄像头能够通过SLAM算法得到小强的位置信息,但是这是在摄像头坐标系下的坐标。通常摄像头坐标系和小车坐标系只是差了一个平动和绕竖直轴的转动。但是如果摄像头放歪了,比如向上歪或向下歪。那么小车移动的最终轨迹在摄像头坐标系下就是一个倾斜的平面。我们要在这个平面内画出目标的导航轨迹,首先就要把这个轨迹放平。那就要知道轨迹平面的法向量。怎么计算这个法向量呢?
我们对这个问题进行数学抽象。三维空间有很多点,这些点大致的分布在一个平面上,我们要找到这个平面的法向量。
最直接的方法就是,利用三点确定一个平面的方式,计算出所有点在平面的组合,然后计算这些平面的平均法向量, 类似于最小二乘法的过程。但是如果有上千个点的话,这个计算量就能达到100010001000。
如果采用模拟退火算法的话就很容易了。
我们开始随便取一个法向量方向,然后计算所有点和法平面的距离。如果我们法向量取得好的话,那么所有点到法平面的距离应该差不多。我们可以通过计算这些距离的方差来描述这个差别。
计算一次之后,我们对当前的法向量进行一次随机的转动,然后用新的法向量再次计算这个方差。如果新的方差更小,我们就用新的法向量进行下一次计算。直到计算出比较满意的结果。下面就是具体的计算代码
var getDirection = (pointsList) => { var noUpdateCount = 0; var direction = null; var planePoint = [0,0,0]; // 计算这些点的中心点 for(let pointIndex in pointsList){ planePoint[0] += pointsList[pointIndex][0]; planePoint[1] += pointsList[pointIndex][1]; planePoint[2] += pointsList[pointIndex][2]; } planePoint[0] = planePoint[0] / pointsList.length; planePoint[1] = planePoint[1] / pointsList.length; planePoint[2] = planePoint[2] / pointsList.length; // 计算评价函数 var res = 0; var planeDirection = [Math.random(), Math.random(), Math.random()]; // 刚开始的随机方向 planeDirectionLength = distance3d(planeDirection, [0,0,0]); planeDirection = [planeDirection[0] / planeDirectionLength, planeDirection[1] / planeDirectionLength, planeDirection[2] / planeDirectionLength]; // 归一化 let scale = 0; let calcCount = 0 while(noUpdateCount < 5 || scale < 1000){ // 结束条件 scale += 1; // 小转动 var rotation = new THREE.Euler( Math.random() * 2 * Math.PI * Math.exp(- scale / 100), Math.random() * 2 * Math.PI * Math.exp(- scale / 100), Math.random() * 2 * Math.PI * Math.exp(- scale / 100), 'XYZ' ); var currentVector = new THREE.Vector3(planeDirection[0], planeDirection[1], planeDirection[2]); var currentPlaneDirectionVector = currentVector.applyEuler(rotation); var currentPlaneDirection = currentPlaneDirectionVector.toArray(); let currentRes = valueFunc(pointsList, currentPlaneDirection, planePoint); //计算评价函数 if(res == 0 || currentRes < res){ // 找到了更好的解 res = currentRes; // 用新解替换旧的值 planeDirection = currentPlaneDirection; console.log('scale', scale); console.log('res', res); noUpdateCount = 0; }else{ noUpdateCount += 1; } } //保证法向量朝上 if(planeDirection[2] < 0){ planeDirection[0] = -planeDirection[0]; planeDirection[1] = -planeDirection[1]; planeDirection[2] = -planeDirection[2]; } return planeDirection; } function calPointToPlaneDistance(planeDirection, planePoint, targetPoint){ return Math.abs(planeDirection[0] * (targetPoint[0] - planePoint[0]) + planeDirection[1] * (targetPoint[1] - planePoint[1]) + planeDirection[2] * (targetPoint[2] - planePoint[2])); } function valueFunc(pointsList, planeDirection, planePoint){ var res = []; for(let pointIndex in pointsList){ res.push(calPointToPlaneDistance(planeDirection, planePoint, pointsList[pointIndex])); } return diffSq(res); } function diffSq(data){ let total = 0; for(let dataIndex in data){ total += data[dataIndex]; } let avg = total / data.length; let res = 0; for(let dataIndex in data){ res += (data[dataIndex] - avg) * (data[dataIndex] - avg); } return res; } function distance3d(pos1, pos2){ return Math.sqrt((pos1[0] - pos2[0]) * (pos1[0] - pos2[0]) + (pos1[1] - pos2[1]) * (pos1[1] - pos2[1]) + (pos1[2] - pos2[2]) * (pos1[2] - pos2[2])); }
运行效率也是不错的
最后法向量的值也是比较准的。看来摄像头基本没有倾斜,摄像头坐标系基本和小车本体坐标系是重合的。
-
公司的大logo
./oh/ ////////////:` `+mMMM+ MMMMMMMMMMMMMNy. .yNMMMMy MMMMMMMMMMMMMMMN/ .sMMMMMMd` MMMMMMMMMMMMMMMMM/ `oNMMMMMMN- MMMMMMMMMMMMMMMMMm` /NMMMMMMMMd MMMMMMMMMMMMMMMMMM: .yMMMMMMMMMM+ MMMMMMMMMMMMMMMMMM+ /mMMMMMMMMMMM- MMMMMMMMMMMMMMMMMMs `sMMMMMMMMMMMMM: MMMMMMMNoosNMMMMMMs .dMMMMMMMMMMMMMMo MMMMMMMm` +MMMMMMs ```````` ````````` `........ +NMMNmmmmmNMMMMMMm` MMMMMMMm` /MMMMMM/ /MMMMMMN- /MMMMMMMo -MMMMMMMy `yMMy:` yMMMMMMM: MMMMMMMm`.+mMMMMMm` /MMMMMMN- /MMMMMMMo -MMMMMMMy oMN: yMMMMMMM: MMMMMMMm`sMMMMMMM+ /MMMMMMN- :MMMMMMMo -MMMMMMMy sMs yMMMMMMN. MMMMMMMm`sMMMMMMo /MMMMMMN- :MMMMMMMo -MMMMMMMy sM- hMMMMMMd` MMMMMMMm`sMMMMMh. :MMMMMMN- :MMMMMMMo -MMMMMMMy sN` hMMMMMM: MMMMMMMm`sMMMMMMN+ :MMMMMMN- :MMMMMMMo -MMMMMMMy yN` /mmmdNMMMMN+ MMMMMMMm`sMMMMMMMM/ :MMMMMMN- :MMMMMMMo -MMMMMMMy yN` :dmmmMMMMN: MMMMMMMm`sMMMMMMMMm. :MMMMMMN- :MMMMMMM+ -MMMMMMMy yN` .NMMm. MMMMMMMm`sMMMMMMMMMo -NMMMMMN- :MMMMMMM+ -MMMMMMMy yN` .ddd/ MMMMMMMm`+yymMMMMMMd -NMMMMMN- :MMMMMMM+ -MMMMMMMy yN` `NMMN/ MMMMMMMm` .NMMMMMm .NMMMMMN- -MMMMMMM+ -MMMMMMMy yN` `` `NMMMMo MMMMMMMm` `NMMMMMN` .NMMMMMN- -MMMMMMM+ -MMMMMMMy yN` hNNNNMMMMMM/ MMMMMMMm`.:/hMMMMMMN .NMMMMMN- -MMMMMMM+ -MMMMMMMy yN` +hhhdNMMMMMm` MMMMMMMm`yMMMMMMMMMm .NMMMMMMh+dMMMMMMMdohMMMMMMMh hN` hMMMMMM+ MMMMMMMm yMMMMMMMMMh .NMMMMMMMMMMMMMMMMMMMMMMMMMMy hM: hMMMMMMd MMMMMMMm yMMMMMMMMM: `dMMMMMMMMMMMMMMMMMMMMMMMMMM: /Mh hMMMMMMd MMMMMMMm yMMMMMMMMd` /MMMMMMMMMMMMhmMMMMMMMMMMMs sNo` dMMMMMMy MMMMMMMm yMMMMMMMN/ /NMMMMMMMMN/ -mMMMMMMMMNo `sMmo-----:mMMMMMM: MMMMMMMm yMMMMMMN/ .odNMMMmo` `odMMMMmo. sMMMMMMMMMMMMMMd MMMMMMMm yMMMMdo` `.` `.` oNMMMMMMMMMMMMs :::::::- -:::. :mMMMMMMMMMMMy `yMMMMMMMMMMh /mMMMMMMMMM. `sNMMMMMMMs -hMMMMMMN. /mMMMMMd` `+dNMMMh` `/yNMy `:s+
小一点的logo
......` .oh/ MMMMMMMh` -hMMy MMMMMMMMd `yMMMN` MMMMMMMMM- /NMMMMy MMMMhmMMM: `yMMMMMMh MMMN -MMM- `sss:`sss+`ssso -mh+++mMMN` MMMN-mMMm .MMMs.MMMh.MMMm d+ mMMM. MMMN/MMN. .MMMs.MMMh.MMMm d` mMMd MMMN/MMMm` .MMMs.MMMh.MMMm d` .mmMMd. MMMN/MMMMy .MMMs.MMMh.MMMm d` `mh` MMMN./hMMN `MMMs.MMMh.MMMm d` `MM+ MMMN`.yMMN `MMMs.MMMh.MMMm d` :mmMMM/ MMMN/MMMMm `NMMNdMMMNdMMMm m` mMMd MMMN/MMMMo dMMMMMNMMMMMMo os mMMm MMMN/MMMd` `yNMNy`:mMMm+ smyyyNMMo yyys-ys/ ` ` +NMMMMM: -dMMMM+ +NMMm` `yNMy :s+
长logo
`/y: `:hMd. `+mMMd` +yyyyys+. `+mMMMN. yMMMMMMMN/ :dMMMMMy yMMMMMMMMN ` `yMMMMMMMy yMMMdsNMMM .++- .::`.::` `:+++`/++.++/`++/.oo-.oo: .shdy- :oo- -dhs+/yMMMN yMMMs-dMMh /MM+ oMM:oMM: yMMMM.dMM:MMM.MMd/MMo/MMs`dMMMMN.sMM/ /Ny` .oMMMN yMMMsmMMN. /MM+ oMM:oMM:.MMMmm.dMM:MMM.MMd/MMo/MMs:MMmdMM/sMM/ hM+``:+yMMMo yMMMsmMMMy`/MM+ oMM:oMM::MMN+/ dMM:MMM.MMd/MMdsMMs/MM++MMosMM/ hM+ -oydMNo` yMMMsymMMM+/MM+ oMM:oMM::MMMMM dMM:MMM.MMd/MMMhMMs/MMhsMMosMM/ hMo `..sMd. yMMMs./NMMh/MMo oMM:oMM::MMmss dMM:MMM-MMd/MMdsMMs/MMMhMMosMM/ hM/ -yymMMm- yMMMshNMMMy:MMm/+MMhdMM-.MMNhy.dMMdMMMhMMd/MMo/MMs/MMdsMMo+MMm/yM/ -//yMMMd yMMMsmMMMN-`mMMy.NMMMMm` dMMMM.oMMMMmMMMMo/MMo/MMs/MM++MMo.NMMo`hh. .oMMMM yNNNsdNmh- .ydo :dmmy. `+syy.`sdds.sdho`-ss:-ss/-ss:-ss: -yh/ `ods+oyMMMm `...`..` ` ``` `` `` :mMMMMMMs .sMMMMMh -hMMMM- :hMMm. -sNm- `/s/
██████╗ ██╗ ██╗ ██╗███████╗██╗ ██╗██╗ ██╗ █████╗ ██╗ ███████╗ ██╔══██╗██║ ██║ ██║██╔════╝██║ ██║██║ ██║██╔══██╗██║ ██╔════╝ ██████╔╝██║ ██║ ██║█████╗ ██║ █╗ ██║███████║███████║██║ █████╗ ██╔══██╗██║ ██║ ██║██╔══╝ ██║███╗██║██╔══██║██╔══██║██║ ██╔══╝ ██████╔╝███████╗╚██████╔╝███████╗╚███╔███╔╝██║ ██║██║ ██║███████╗███████╗ ╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝