tobot

  • 2025-04-02
  • 发表了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.4)

    书接上回(估计鸽了一个月了吧),上次我们从高空丢下一个红球和一个蓝球,现在我们换两个东西丢下来: [localvideo]1bdd8210a344b2175a36449f67eca30a[/localvideo] 一个恐龙玩具和一个人偶 其中恐龙玩具是stl模型,人偶是从mujoco自带的humanoid.xml里的素材“torso”,使用xml模型剪切出来。 附件: 代码如下: import mujoco import mujoco.viewer import time from xml.dom import minidom def load_xml(xml): dom = minidom.parse(xml) return dom def get_root_element(dom): if dom is not None: root = dom.documentElement return root return None def get_child_elements(root,tag): if root is not None: child_elements = root.getElementsByTagName(tag) return child_elements return None def make_object(dom,obj): newnode = dom.createElement("geom") for k,v in obj.items(): newnode.setAttribute(k,v) return newnode def make_object(dom,obj,file_name,scale = "1 1 1"): root = get_root_element(dom) asset = get_child_elements(root,"asset") if asset == []: asset = dom.createElement("asset") root.appendChild(asset) else: asset = asset[0] mesh = dom.createElement("mesh") mesh.setAttribute("file",file_name) mesh.setAttribute("name",obj["mesh"]) mesh.setAttribute("scale",scale) asset.appendChild(mesh) newnode = dom.createElement("geom") for k,v in obj.items(): newnode.setAttribute(k,v) return newnode def add_object_neo(pnode,newnode,geom_in_body_color=None): if newnode.nodeName == "geom": tmpbody = dom.createElement("body") tmpnode = dom.createElement("joint") tmpnode.setAttribute("type","free") tmpbody.appendChild(tmpnode) tmpbody.appendChild(newnode) pnode.appendChild(tmpbody) if newnode.nodeName == "body": if geom_in_body_color: geoms = get_child_elements(newnode,"geom") for geom in geoms: geom.setAttribute("rgba", geom_in_body_color) pnode.appendChild(newnode) def get_body(obj_file,obj_name): obj_dom = minidom.parse(obj_file) obj_nodes = obj_dom.getElementsByTagName("body") for obj_node in obj_nodes: if obj_node.getAttribute("name") == obj_name: return obj_node if __name__ == "__main__": dom = load_xml("tower.xml") root = get_root_element(dom) pnode = get_child_elements(root, "worldbody")[0] Aristotle = get_body("humanoid.xml","torso") Aristotle.setAttribute("pos","16 1.5 31.6") add_object_neo(pnode,Aristotle,"0 0 .9 1") file_name = "triceratops.stl" triceratops = {} triceratops["type"] = "mesh" triceratops["pos"] = "16 -1.5 30" triceratops["mesh"] = "dino" triceratops["rgba"] = ".9 0 0 1" triceratops["euler"] = "90 0 0" newnode = make_object(dom,triceratops,file_name,".08 .08 .08") add_object_neo(pnode,newnode) model = mujoco.MjModel.from_xml_string(dom.toxml()) data = mujoco.MjData(model) viewer = mujoco.viewer.launch_passive(model, data) viewer.cam.azimuth = 145.0 viewer.cam.distance = 90 viewer.cam.elevation = -5.0 while True: mujoco.mj_step(model, data) viewer.sync() time.sleep(0.001) 好困,不想写代码分析了,把代码注释粘下面:          

  • 回复了主题帖: MuJoCo 机械臂物体碰撞、接触检测方式一

    蓝雨夜 发表于 2025-4-2 08:37 下次有没有安排从硬件慢慢搭建,选型,到调试的啊  mujoco啊,我来做教程好了。。。就是很懒,写了几个字就开始摸鱼了。。。

  • 2025-03-25
  • 发表了主题帖: 【新年点灯】总结——Raspberry Pi Pico驱动WS2812全彩LED矩阵

    点灯总结——Raspberry Pi Pico驱动WS2812全彩LED矩阵 利用微控制器驱动LED矩阵实现动态显示,是非常基础也是非常常见的应用了,在本次点灯活动中,基于Raspberry Pi Pico开发板,通过MicroPython编程驱动WS2812全彩LED灯板,成功复现了经典游戏角色及场景动画。 0x01 项目背景与思路 动机溯源 利用业余时间探索Pico的硬件潜能,结合手头WS2812灯板资源,通过低成本方案实现高精度全彩动态显示。 技术可行性分析 WS2812采用单线级联架构,利用非归零编码传输24位RGB数据,天然支持长链级联。Pico的PIO(可编程I/O)引擎配合状态机架构,可以精确显示到每一个像素,且保持较高的刷新速率。   0x02 硬件系统设计   [电源] 5V───┬──────────┬──────────┬──────────┬──────────┬──LED灯带   │ │ │ │ └──(并联结构)   ▼ ▼ ▼ ▼ | LED灯带 | WS2812B 256颗 | 全彩显示单元,级联模式工作 | | 电源模块 | 5V/5A电源 | 独立供电防止主控板过载 | | 信号接口 | 杜邦线+排针 | Pico GP0引脚→LED数据输入端 |   采用rpi的pico的pin0做控制信号,连接在ws2812的输入端;虽然在ws2812板上每颗灯只需要大概0.03A的电流,所有灯珠电源的正负极都是并联起来的,由于使用灯数较多(256颗),因此对电流要求相对较高,专门准备了一个5V5A电源对其供电。在实际使用中,并不是所有灯都全部点亮,因此5A的电流足以驱动。   0x03 软件实现 我们知道WS2812是采用单线通信协议(非归零编码),24位RGB数据依次传输(每LED截取前24bit后转发剩余数据),灯珠的控制信号彼此串联,第一个灯的信号输出就是第二个灯的输入,每过一个灯,去除自己所用的数据,将剩下的bit流给下一个灯珠。 因此只要发出一段连续bit码,就能够实现点亮全部灯,并且由于灯的颜色不同,可以组成不同图案;通过保持供电,图案不会发生变化;在下一时间段内,发送另一段bit码,就能修改图案内容。 因此只需要考虑如何发送这些bit码就可以了,树莓派pico有自己的点灯库,我这里调用的是array、time、machine中的Pin等,都是现成的。 第一步是把树莓派pico改造成mpython支持,将MicroPython固件刷入Raspberry Pi Pico。 第二步是程序设计,框架基本类似,都是 定义PIO程序‌:使用@rp2.asm_pio 装饰器定义一个PIO程序,用于生成WS2812灯的控制信号。 创建StateMachine‌:使用rp2.StateMachine创建状态机,将PIO程序与指定引脚关联。 ‌启动StateMachine‌:调用sm.active(1)启动状态机。 第三步是将想显示的图像转存为数组,由于四块ws2812的灯板连接方式比较特别,如下:   所以我专门编写了一段代码来实现这个工作,将16*16的图像转到字节组中,再显示出来。 第四步,将py文件置入pico中。 第五步,加电测试。   0x04 动画效果 如前面所述,动画效果的实现只需要更换bit流就可以了,因此准备多幅图片替换显示。在ghost项目中,增加了随机值,由于每次更换的图片相对随机,所以把图片拆解成多个部分,在程序中重新组合起来。   0x05 成果帖和源文件下载 宝可梦系列中的经典精灵球: https://bbs.eeworld.com.cn/thread-1305846-1-1.html 塞尔达传说的主角林克: https://bbs.eeworld.com.cn/thread-1305950-1-1.html https://bbs.eeworld.com.cn/thread-1306139-1-1.html FC食豆人中的第1位敌人——Blinky https://bbs.eeworld.com.cn/thread-1306543-1-1.html   补充: 游戏中的经典形象我都是手绘以后再转储的,下次如果有机会拿个解析更高的显示,可以搞sonic和tails……

  • 2025-03-09
  • 回复了主题帖: 【新年花灯秀】彩色PCB雪花灯

    漂亮

  • 2025-03-01
  • 回复了主题帖: 想知道两轮差速方形底盘 URDF 咋做,ROS2 配 Rviz 咋显示吗?看这里!

    学习学习

  • 2025-02-28
  • 回复了主题帖: 跟我学:每天5分钟,一个月入门mujoco(序)

    freebsder 发表于 2025-2-28 16:29 这是个仿真平台还是个开发平台呢? 可以把这个当作是一个游戏引擎:把一个完整游戏里面的素材都删掉,但保留了接口。   就像马里奥制造那样的,各种砖块都放在那里,你想搭一个什么关卡都可以,等你搭好了,就可以尝试过关(类似仿真),但是你也可以你的存档给别人,别人可以在你基础上继续修改(类似开发)。

  • 2025-02-27
  • 回复了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.3)

    jryq213 发表于 2025-2-27 12:33 非常错的资料,学习了,如果有详细课件更好。 有源码,全套代码我是我自己敲的。 需要ppt么?那可能得重新写

  • 回复了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.3)

    wangerxian 发表于 2025-2-27 08:57 可以配置小球的密度或者材质吗? 可以的,下下下……节就有了 下节是初速度 下下节是采集数据和生成曲线 再往后就是改变各种外部参数,比如重力、风力(风阻)、密度等等

  • 2025-02-26
  • 发表了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.3)

    第3节 自由落体 在以比萨斜塔为背景,让两个小球从高处落下: [localvideo]f194ab1f63194a35a1dd18fae011e4a6[/localvideo]   可以看到,存在一个加速下落的过程。 代码如下: import mujoco import mujoco.viewer import time from xml.dom import minidom def load_xml(xml): dom = minidom.parse(xml) return dom def get_root_element(dom): if dom is not None: root = dom.documentElement return root return None def get_child_elements(root,tag): if root is not None: child_elements = root.getElementsByTagName(tag) return child_elements return None def make_object(dom,obj): newnode = dom.createElement("geom") for k,v in obj.items(): newnode.setAttribute(k,v) return newnode def add_object(dom,pnode,newnode): if newnode.nodeName == "geom": tmpbody = dom.createElement("body") tmpnode = dom.createElement("joint") tmpnode.setAttribute("type","free") tmpbody.appendChild(tmpnode) tmpbody.appendChild(newnode) pnode.appendChild(tmpbody) if __name__ == "__main__": red_ball = {} red_ball["type"] = "sphere" red_ball["pos"] = "16 -1.5 30" red_ball["size"] = "1" red_ball["rgba"] = ".9 0 0 1" blue_ball = {} blue_ball["type"] = "sphere" blue_ball["pos"] = "16 1.5 30" blue_ball["size"] = "1" blue_ball["rgba"] = "0 0 .9 1" dom = load_xml("tower.xml") root = get_root_element(dom) pnode = get_child_elements(root, 'worldbody')[0] newnode = make_object(dom,red_ball) add_object(dom,pnode,newnode) newnode = make_object(dom,blue_ball) add_object(dom,pnode,newnode) model = mujoco.MjModel.from_xml_string(dom.toxml()) data = mujoco.MjData(model) viewer = mujoco.viewer.launch_passive(model, data) viewer.cam.azimuth = 145.0 viewer.cam.distance = 90 viewer.cam.elevation = -5.0 while True: mujoco.mj_step(model, data) viewer.sync() time.sleep(0.001) 分析: 这里有两个函数 def make_object(dom,obj):     newnode = dom.createElement("geom")     for k,v in obj.items():         newnode.setAttribute(k,v)     return newnode   def add_object(dom,pnode,newnode):     if newnode.nodeName == "geom":         tmpbody = dom.createElement("body")         tmpnode = dom.createElement("joint")         tmpnode.setAttribute("type","free")         tmpbody.appendChild(tmpnode)         tmpbody.appendChild(newnode)         pnode.appendChild(tmpbody)   make_object是用于生成一个新的geom(几何体),是将刚生成的几何体放置在之前的dom中。 在前面章节里面,我们知道,如果不做设置,同一个body内的的几何体相对位置是固定的,也就是说,即使我们放置了geom,它也会悬在半空。为了实现下落的要求,我们增加了一个属性类型为”free”的joint(关节),这里新引入了joint的概念,它是用于连接几何体的虚拟构件。主要属性有: name:名字 pos:关节位置 type:关节类型,可以是hinge(铰链关节,绕单一轴旋转)、slide(滑动关节,沿单一轴线移动)、ball(球形关节)、free(自由型),缺省是hinge axis:轴,可以用于滚动(hinge)或者滑动slide

  • 2025-02-25
  • 回复了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.2)

    wangerxian 发表于 2025-2-25 20:39 这是一个3D库吗?可以导入什么样的3D文件? 事实上,它的3D功能好像还真不怎么样,基础功能只有方块、球、椭球、连三角块都需要外部建模,支持stl格式的mesh导入…… 而且渲染功能我一直没搞定,但物理特性仿真不错(特指固体)。

  • 2025-02-24
  • 发表了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.2)

    第2节 设置视角 在前1节,我们建立了模型并且将其存储为一个xml文件,但我们发现,这个模型相对我们的显示界面“太大”了,需要用鼠标调整以后才能摆在屏幕中间,至于怎么调整,相信大家都很熟悉,左键拖动旋转,右键移动位置,滚轮缩放。 在本节,我们复习载入xml文件,并学习在代码中变换视角。 [localvideo]67a900b20b8bda66810078b99025e377[/localvideo] 代码其实非常简单,就是不断变换几个参数: import mujoco import mujoco.viewer import time if __name__ == "__main__": model = mujoco.MjModel.from_xml_path('tower.xml') data = mujoco.MjData(model) viewer = mujoco.viewer.launch_passive(model, data) for i in range(0,960): viewer.cam.elevation= 0 - i/65 viewer.cam.distance = i/15+40 viewer.cam.azimuth = i/2 viewer.cam.lookat = [0, 0, -15+i/55] time.sleep(0.02) 由于代码实在太简单,本节不做代码讲解。

  • 回复了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.1)

    hellokitty_bean 发表于 2025-2-24 08:57 让人好好奇呀。。。。。。。。。。mujoco这么可爱好玩吗 如果当游戏玩的话,建议“马里奥制造”,如果想用来当物理学教具,我认为初中的所有实验都够了

  • 2025-02-23
  • 发表了主题帖: 跟我学:每天5分钟,一个月入门mujoco(1.1)

    重力加速度 在本章中,我们尝试编写一个模型测试物体在重力场中的行为,并尝试修改各项参数(质量、形状、重力加速度等),看看如何在mujoco的呈现。   第1节 坐标系与参照物 说起重力加速度,最出名的肯定是伽利略先生在比萨斜塔上做的自由落体实验。多年前我也曾前往斜塔朝圣,这里就以斜塔作为落体的参照物吧。 这一节我们先学着画参照物:   (照片来自网络,侵权请告知) 可以看到塔分8层,每层的结构相似,由柱子和塔身构成,可以抽象为使用若干大大小小的圆柱组合而成。   如前一章节中介绍,可以使用xml格式文件来存储mujoco模型。那么现在就介绍一下mujoco里面常用的xml标签元素: mujoco: 这是最外层的标签元素,也就是所谓的根,全xml只能有一个,有属性值model,缺省是“MuJoCo Model”,在viewer中显示的标题。 body(体): body是在mujoco中用于指定物体的标签元素(支持套嵌,最外层的叫做worldbody),可以包括的属性有: name:体的名字,定义后可以用name来引用这个体 pos:体的位置,当然是基于上级元素的,缺省是(0,0,0) euler:控制体的方向,也是基于上级元素的 Geom(几何体): name:名字 pos:几何体位置 type:几何体类型,可以是sphere(球体,缺省值)、plane(地平面)、box(立方体)、cylinder(圆柱体)、capsule(胶囊体)、ellipsoid(橄榄球形)等等 size:几何体大小 rgba:渲染几何体的颜色     知道了上述信息,就能开始了,首先,我们具有简单python基础,编写xml文件可以用dom。 import mujoco import mujoco.viewer import xml.dom.minidom as minidom import math def create_root(dom): root = dom.documentElement root.setAttribute("model", "Elizabeth Tower") return root def create_world(dom,root): worldbody = dom.createElement("worldbody") root.appendChild(worldbody) plane = dom.createElement("geom") plane.setAttribute("type","plane") plane.setAttribute("size","20 20 0.1") plane.setAttribute("rgba","0 .8 0 1") plane.setAttribute("pos","0 0 -22") ground = dom.createElement("geom") ground.setAttribute("type","box") ground.setAttribute("name","ground") ground.setAttribute("size","20 20 1") ground.setAttribute("rgba",".237 .189 .101 1") ground.setAttribute("pos","0 0 -23") worldbody.appendChild(plane) worldbody.appendChild(ground) towerbody = create_tower(dom) towerbody.setAttribute("pos","0 0 -23") towerbody.setAttribute("euler","0 5 0") worldbody.appendChild(towerbody) def create_tower(dom): towerbody = dom.createElement("body") towerbody.setAttribute("name","Elizabeth Tower") towerbase = dom.createElement("geom") towerbase.setAttribute("type","cylinder") towerbase.setAttribute("rgba",".5 .5 .5 1") towerbase.setAttribute("pos","0 0 5") towerbase.setAttribute("size","9 5") towerbody.appendChild(towerbase) for i in range(15): a=11 pole = dom.createElement("geom") pole.setAttribute("type","cylinder") pole.setAttribute("rgba",".5 .5 .5 1") pole.setAttribute("pos",str(a*math.sin(i*math.pi/7.5))+" "+str(a*math.cos(i*math.pi/7.5))+" 5") pole.setAttribute("size",".7 5") towerbody.appendChild(pole) top1 = dom.createElement("geom") top1.setAttribute("type","cylinder") top1.setAttribute("rgba",".5 .5 .5 1") top1.setAttribute("pos","0 0 10") top1.setAttribute("size","12.1 .1") towerbody.appendChild(top1) a = 10.8 for level in range(1,7): top = dom.createElement("geom") top.setAttribute("type","cylinder") top.setAttribute("rgba",".5 .5 .5 1") top.setAttribute("pos","0 0 "+str(10+level*6)) top.setAttribute("size",str(a+0.8)+" .1") towerbody.appendChild(top) for i in range(30): pole = dom.createElement("geom") pole.setAttribute("type","cylinder") pole.setAttribute("rgba",".5 .5 .5 1") pole.setAttribute("pos",str(a*math.sin(i*math.pi/15))+" "+str(a*math.cos(i*math.pi/15))+" "+str(7+6*level)) pole.setAttribute("size",".5 3") towerbody.appendChild(pole) stair = dom.createElement("geom") stair.setAttribute("type","cylinder") stair.setAttribute("rgba",".5 .5 .5 0.5") stair.setAttribute("pos","0 0 "+str(7+6*level)) stair.setAttribute("size",str(a-2)+" 3") towerbody.appendChild(stair) a=a-0.15 towertop = dom.createElement("geom") towertop.setAttribute("type","cylinder") towertop.setAttribute("rgba",".5 .5 .5 1") towertop.setAttribute("pos","0 0 50") towertop.setAttribute("size","7 4") towerbody.appendChild(towertop) for i in range(12): a=8 pole = dom.createElement("geom") pole.setAttribute("type","cylinder") pole.setAttribute("rgba",".5 .5 .5 1") pole.setAttribute("pos",str(a*math.sin(i*math.pi/6))+" "+str(a*math.cos(i*math.pi/6))+" 50") pole.setAttribute("size",".4 4") towerbody.appendChild(pole) top2 = dom.createElement("geom") top2.setAttribute("type","cylinder") top2.setAttribute("rgba",".5 .5 .5 1") top2.setAttribute("pos","0 0 54") top2.setAttribute("size","9 .3") towerbody.appendChild(top2) return towerbody if __name__ == "__main__": dom = minidom.getDOMImplementation().createDocument(None,"mujoco",None) root = create_root(dom) create_world(dom,root) m = mujoco.MjModel.from_xml_string(dom.toxml()) v = mujoco.viewer.launch(m) with open('tower.xml', 'w', encoding='utf-8') as f: dom.writexml(f, addindent='\t', newl='\n',encoding='utf-8')   执行一下看看,嗯,由于模型太大了,可能看不到全图,可以用鼠标滚轮操作一下缩放:     代码分析: dom = minidom.getDOMImplementation().createDocument(None,"mujoco",None) 新建dom,其中根叫做mujoco root = dom.documentElement root.setAttribute("model", "Elizabeth Tower") 获取根节点,指定model属性(显示名为Elizabeth Tower)。 worldbody = dom.createElement("worldbody") root.appendChild(worldbody) 在root下增加worldbody。 with open('tower.xml', 'w', encoding='utf-8') as f:         dom.writexml(f, addindent='\t', newl='\n',encoding='utf-8') 保存xml文件,命名为tower.xml。

  • 2025-02-21
  • 回复了主题帖: 恭喜LitchiCheng走马上任机器人开发版块版主!!

    恭喜恭喜

  • 发表了主题帖: 跟我学:每天5分钟,一个月入门mujoco(序)

    机器人仿真是一个很有趣的话题,多年前我曾尝试用过不少游戏引擎去学习“制作”机器人,但最终比较倾向于使用webots和mujoco。 这里可以开两个两个讨论话题:webots、mujoco。 Mujoco也是比较神奇的,最初免费,不少人学习使用;后来收费,损失了大量人气,再后来被deepmind买下,直接开源了,但已经不复当年盛况,在中文的各论坛上也基本找不到什么新一点有深度的资料——几年前的还有,但已经不再适配最新版本了。 有鉴于此,笔者准备为爱发电,借eeworld宝地开一个mujoco系列。大家可以试着跟读,每天只占用5分钟阅读时间,大概一个月,就能把这个有趣物理引擎玩转了。当然我不保证每天都能更新。 mujoco是什么? Multi-Joint dynamics with Contact,翻译起来可以认为是多关节动力学连接。这也是mujoco的主要用途。可以快速开发多个物体的连接,并且对其物理特性进行仿真。具体使用什么语言去开发,其实并不重要,看大家的习惯吧。在本系列的文章中,我们从实战入手,使用python进行开发,且假设各位都已经有一点点python的开发基础了。     第0章 安装环境和显示一个mujoco模型 在本章的练习中,我们将使用到mujoco的传统例子——机械手臂。 首先,我们准备一下开发环境,使用pip install mujoco安装mujoco的python支持(这里最好是python3.10以上的版本,本人使用的是python3.11)。需要用到的xml文件(arm26.xml)和本节所用到的代码见附件。 我们先介绍一下几个基础概念: 模型 在mujoco中,模型是可以用于定义显示和操作的,可以使用xml存储。 视图 Viewer是mujoco最重要的组成部分,用于呈现出界面。一般来说,我们可以通过命令行调用: python -m mujoco.viewer --mjcf=arm26.xml 执行该命令后,可以看到一个所谓2连接6肌肉的手臂,可以通过按钮来控制这个手臂 [localvideo]e1308ec00eb9ee7d1b90c454b7ab00e9[/localvideo]         用代码载入 #直接pip install mujoco安装 import mujoco.viewer m = mujoco.MjModel.from_xml_path('arm26.xml') v = mujoco.viewer.launch(m)   核心就两条,因为太过简单,就不怎么介绍了吧 m = mujoco.MjModel.from_xml_path('arm26.xml') v = mujoco.viewer.launch(m)   本章节用到模型xml文件  

  • 回复了主题帖: 撒积分啦!!机器人开发圈公众号上线、还有多个新板块设立哦~~

    恭喜恭喜

  • 回复了主题帖: 四足机器人学习资料

    freebsder 发表于 2025-2-20 19:21 arduino控制的?应该没那么智能吧 确实是arduino。。。

  • 2025-02-20
  • 上传了资料: 四足机器人

  • 发表了主题帖: 四足机器人学习资料

    上传两个我珍藏模型——以前一直以为有时间能弄出来玩玩的,一拖就是几年 [localvideo]187a7cfaf2d55df5399c29f4a5496992[/localvideo]    

  • 回复了主题帖: 【过年都玩啥】话说探友们过年都玩啥了呢?

    回乡炸牛屎。。。

最近访客

< 1/6 >

统计信息

已有370人来访过

  • 芯积分:1870
  • 好友:5
  • 主题:118
  • 回复:314

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言