当前位置:   article > 正文

Mujoco学习记录_mujoco和webots

mujoco和webots

本文档为AIR DISCOVERY LAB拾遗。当时的我还没变强。现在的我开始回顾,当时的一些文档依然有一定的意义。

概览

Mujoco是类似Webots的一款仿真软件,用于Linux系统中。

Mujoco官网:MuJoCo — Advanced Physics Simulation

Mujoco Documentation:Overview - MuJoCo Documentation

Mujoco Git:GitHub - google-deepmind/mujoco: Multi-Joint dynamics with Contact. A general purpose physics simulator.

安装:

本人采用的源码安装方法

源码安装的更多细节,请参考:Programming - MuJoCo Documentation

学习

入门的过程我参考的这篇内容:https://colab.research.google.com/github/deepmind/mujoco/blob/main/python/tutorial.ipynb#scrollTo=T5f4w3Kq2X14

这是一个ipynb格式的文档。这个文档支持用户在线实时运行代码,因此是一个学习的非常好的平台。

入门上手

Hello World

首先,类似于Hello World,Mujoco有一个能够让你快速入门的代码:

  1. xml = """
  2. <mujoco>
  3. <worldbody>
  4. <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
  5. <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
  6. </worldbody>
  7. </mujoco>
  8. """
  9. model = mujoco.MjModel.from_xml_string(xml)

mjModel

Mujoco本质上用的是MJCF文本来定义Mujoco模型的。Mujoco类似于urdf使用的xml格式。其中<mujoco>和<worldbody>两个标签是必须要的。

在model通过Mjmodel绑定模型之后,便可以通过mjmodel调取模型的一些参数了,比如说:model的名称、rgb信息、数量等。

不过在你调取model.geom之后,它的返回值是包含该几何体所有信息的一个结构体。你也可以通过地址访问的方法读取结构体中的所需信息。

mjData

mjData是model在仿真环境中的信息,由时间、位移和速度组成。我们将data和model绑定之后,就可以通过mjData读取mjModel在仿真中的各种信息了。除此之外,mjData还可以提供物体在世界坐标系下的位置信息。

但同时,如果我们要用到全局的坐标信息,就需要使用mj_kinematics函数了,它可以提供全局笛卡尔坐标系的信息。

基础仿真

在仿真之前,我们需要使用render函数将我们的模型渲染出来。在渲染之前,一般需要做如下几件事情:

  1. model # 定义模型
  2. data # 传输数据
  3. mujoco.mj_forward(model, data) # 将这二者传入render中

renderer函数一般常用以下三个内容:

  1. renderer = mujoco.Renderer(model) # 初次调用
  2. renderer.update_scene(data) # 更新渲染图
  3. media.show_image(renderer.render()) # 打印渲染图

好了。在render之后,我们可以进行simulation了。simulation最关键的一个函数是mj_step,它能够按照每一步来进行仿真。

  1. xml = """
  2. <mujoco>
  3. <worldbody>
  4. <light name="top" pos="0 0 1"/>
  5. <body name="box_and_sphere" euler="0 0 -30">
  6. <joint name="swing" type="hinge" axis="1 -1 0" pos="-.2 -.2 -.2"/>
  7. <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
  8. <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
  9. </body>
  10. </worldbody>
  11. </mujoco>
  12. """
  13. model = mujoco.MjModel.from_xml_string(xml)
  14. data = mujoco.MjData(model)
  15. renderer = mujoco.Renderer(model)
  16. # enable joint visualization option:
  17. scene_option = mujoco.MjvOption()
  18. scene_option.flags[mujoco.mjtVisFlag.mjVIS_JOINT] = True
  19. duration = 3.8 # (seconds)
  20. framerate = 60 # (Hz)
  21. frames = []
  22. mujoco.mj_resetData(model, data)
  23. while data.time < duration:
  24. mujoco.mj_step(model, data)
  25. if len(frames) < data.time * framerate:
  26. renderer.update_scene(data, scene_option=scene_option)
  27. pixels = renderer.render()
  28. frames.append(pixels)
  29. # Simulate and display video.
  30. media.show_video(frames, fps=framerate)

这个是比较基本的一段仿真代码。仿真时,需要先设定duration(视频时长)和framerate(帧率)。然后调用mj_step计算仿真,并调用renderer再次产生结果。同时需要注意,我们在仿真时要加入joints,并且设定joint格式才能够让环境动起来,否则没有DoF(自由度)机构是动不起来的。使能关节时,我们使用函数MjvOption。

在仿真时,可以改变一些物理参数来调整仿真结果,例如gravity和Friction(摩擦力)。

DoF

DoF,即自由度。真实世界中自由度是由xyz三坐标和rpy三轴旋转组成的。现实世界中的joints充当限制自由度的元素。但Mujoco使用joint来定义自由度。即:没有关节的地方默认不存在自由度

Tipper-top仿真示例

接下来是对tipper-top的一个仿真实例。tipper-top是一个能够自己旋转的类似尖顶的玩具。示意视频如下:

https://youtu.be/kbYpVrdcszQ

仿真代码如下:

  1. tippe_top = """
  2. <mujoco model="tippe top">
  3. <option integrator="RK4"/> # 选择积分器为RK4
  4. <asset>
  5. <texture name="grid" type="2d" builtin="checker" rgb1=".1 .2 .3"
  6. rgb2=".2 .3 .4" width="300" height="300"/>
  7. <material name="grid" texture="grid" texrepeat="8 8" reflectance=".2"/>
  8. </asset> # 用于宏定义一个地板的结构和材料
  9. <worldbody>
  10. <geom size=".2 .2 .01" type="plane" material="grid"/>
  11. <light pos="0 0 .6"/>
  12. <camera name="closeup" pos="0 -.1 .07" xyaxes="1 0 0 0 1 2"/>
  13. <body name="top" pos="0 0 .02">
  14. <freejoint/> #设定关节为自由旋转关节,有6自由度
  15. <geom name="ball" type="sphere" size=".02" />
  16. <geom name="stem" type="cylinder" pos="0 0 .02" size="0.004 .008"/>
  17. <geom name="ballast" type="box" size=".023 .023 0.005" pos="0 0 -.015"
  18. contype="0" conaffinity="0" group="3"/>
  19. </body>
  20. </worldbody>
  21. <keyframe>
  22. <key name="spinning" qpos="0 0 0.02 1 0 0 0" qvel="0 0 0 0 1 200" />
  23. </keyframe> # 定义初始位置和速度
  24. </mujoco>
  25. """
  26. model = mujoco.MjModel.from_xml_string(tippe_top)
  27. renderer = mujoco.Renderer(model)
  28. data = mujoco.MjData(model)
  29. mujoco.mj_forward(model, data)
  30. # renderer.update_scene(data, camera="closeup")
  31. # media.show_image(renderer.render())
  32. duration = 7 # (seconds)
  33. framerate = 60 # (Hz)
  34. # Simulate and display video.
  35. frames = []
  36. mujoco.mj_resetDataKeyframe(model, data, 0) # Reset the state to keyframe 0
  37. while data.time < duration:
  38. mujoco.mj_step(model, data)
  39. if len(frames) < data.time * framerate:
  40. renderer.update_scene(data, "closeup")
  41. pixels = renderer.render()
  42. frames.append(pixels)
  43. media.show_video(frames, fps=framerate)

其中,qvel就是xyz、rpy;qpos是三个xyz参数,和四元数定义的旋转信息。

仿真中测量信息

mjData中也可以存放仿真的信息,包括速度、加速度等。我们通过读取mjData的值,就可以读取得到其中的信息。并且在Python中,我们还可以通过plt.subplots函数绘制图像。

  1. timevals = []
  2. angular_velocity = []
  3. stem_height = []
  4. # Simulate and save data
  5. mujoco.mj_resetDataKeyframe(model, data, 0)
  6. while data.time < duration:
  7. mujoco.mj_step(model, data)
  8. timevals.append(data.time)
  9. angular_velocity.append(data.qvel[3:6].copy())
  10. stem_height.append(data.geom_xpos[2,2]);
  11. dpi = 120
  12. width = 600
  13. height = 800
  14. figsize = (width / dpi, height / dpi)
  15. _, ax = plt.subplots(2, 1, figsize=figsize, dpi=dpi, sharex=True)
  16. ax[0].plot(timevals, angular_velocity)
  17. ax[0].set_title('angular velocity')
  18. ax[0].set_ylabel('radians / second')
  19. ax[1].plot(timevals, stem_height)
  20. ax[1].set_xlabel('time (seconds)')
  21. ax[1].set_ylabel('meters')
  22. _ = ax[1].set_title('stem height')

其他仿真示例

网站中给出了一个混沌摆的仿真示例。其核心代码基本类似,但其中要设置的每一步的帧率和时间要更多也更复杂。同样,对于混沌摆而言,其中的各种物理规律也是混沌状态。

但需要注意的是,仿真过程中Timestep的设定会影响到仿真的精确度和能量的储存。在仿真时,适当提升精确度可以保证能量的稳定性和提升仿真整体效果。

同样的,仿真时timestep设置的过大,也会造成模型过快发散的问题。

仿真时信息的可视化

在仿真时,MjvOption可以设定某些仿真信息(接触点、接触力)可视化。设定时示例如下:

  1. # visualize contact frames and forces, make body transparent
  2. options = mujoco.MjvOption()
  3. mujoco.mjv_defaultOption(options)
  4. options.flags[mujoco.mjtVisFlag.mjVIS_CONTACTPOINT] = True
  5. options.flags[mujoco.mjtVisFlag.mjVIS_CONTACTFORCE] = True
  6. options.flags[mujoco.mjtVisFlag.mjVIS_TRANSPARENT] = True

执行器(电机)和传感器

Mujoco的执行器和传感器的设置也是在MJCF文档中完成的。

其中,传感器的标签是<sensor/>,执行器的标签是<actuator/>.

在前期的代码配置完成之后,可以在data.ctrl中配置电机旋转速度;利用sensordata.append函数也可以读取我们设定的sensor中的传感器数据。

高级仿真设置

在scene_option中,可以根据我们的需要更改render的部分参数,使得仿真时显示出我们需要的内容。

  1. #@title Enable transparency and frame visualization
  2. # 功能:显示TF坐标变换
  3. scene_option.frame = mujoco.mjtFrame.mjFRAME_GEOM
  4. scene_option.flags[mujoco.mjtVisFlag.mjVIS_TRANSPARENT] = True
  5. #@title Depth rendering
  6. #功能:图像以深度信息的形式显示
  7. # update renderer to render depth
  8. renderer.enable_depth_rendering()
  9. # reset the scene
  10. renderer.update_scene(data)
  11. # depth is a float array, in meters.
  12. depth = renderer.render()
  13. # Shift nearest values to the origin.
  14. depth -= depth.min()
  15. # Scale by 2 mean distances of near rays.
  16. depth /= 2*depth[depth <= 1].mean()
  17. # Scale to [0, 255]
  18. pixels = 255*np.clip(depth, 0, 1)
  19. #@title Segmentation rendering
  20. #功能:将渲染图按照部分来渲染出来
  21. # update renderer to render segmentation
  22. renderer.enable_segmentation_rendering()
  23. # reset the scene
  24. renderer.update_scene(data)
  25. seg = renderer.render()
  26. # Display the contents of the first channel, which contains object
  27. # IDs. The second channel, seg[:, :, 1], contains object types.
  28. geom_ids = seg[:, :, 0]
  29. # Infinity is mapped to -1
  30. geom_ids = geom_ids.astype(np.float64) + 1
  31. # Scale to [0, 1]
  32. geom_ids = geom_ids / geom_ids.max()
  33. pixels = 255*geom_ids

在仿真时,我们同样可以设置相机内参矩阵。

关于相机内参矩阵的定义,请参考这篇维基百科:

Camera matrix

当我们拿到了相机内参矩阵时,我们便可以建立起来三维世界坐标和二维照片坐标之间的联系。

到最后,我们还可以通过Mujoto.scene向视图中添加任意几何体。

因为个人能力原因,相机内参和任意几何体部分对于原理和使用尚存疑惑没看明白。

至此,基础的Mujoco仿真已经学习结束。

实践

搭建一个倒立摆

首先,我上手在python脚本里面复刻代码。但发现好像不能用mediapy中的命令来进行图像输出。于是我决定换个计策。我选用了scikit-image中的功能包。该功能包能够直接保存图片。

测试代码如下:

  1. import mujoco
  2. import os
  3. import subprocess
  4. # import mediapy as media
  5. from skimage import io
  6. xml = """
  7. <mujoco>
  8. <worldbody>
  9. <light name="top" pos="0 0 1"/>
  10. <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
  11. <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
  12. </worldbody>
  13. </mujoco>
  14. """
  15. model = mujoco.MjModel.from_xml_string(xml)
  16. data = mujoco.MjData(model)
  17. renderer = mujoco.Renderer(model)
  18. mujoco.mj_forward(model, data)
  19. renderer.update_scene(data)
  20. io.imshow(renderer.render())
  21. io.imsave('data1.jpg',renderer.render())

图像可以用这个功能包解决,但输出的短视频应该如何解决呢?

经过查找资料,短视频输出就用cv2功能包来做就可以。主要设置:输出大小、输出帧率、输出编码格式,然后按帧输出就可以了。

测试代码如下:

  1. import mujoco
  2. import os
  3. import subprocess
  4. # import mediapy as media
  5. # from skimage import io
  6. import cv2
  7. xml = """
  8. <mujoco>
  9. <worldbody>
  10. <light name="top" pos="0 0 1"/>
  11. <body name="box_and_sphere" euler="0 0 -30">
  12. <joint name="swing" type="hinge" axis="1 -1 0" pos="-.2 -.2 -.2"/>
  13. <geom name="red_box" type="box" size=".2 .2 .2" rgba="1 0 0 1"/>
  14. <geom name="green_sphere" pos=".2 .2 .2" size=".1" rgba="0 1 0 1"/>
  15. </body>
  16. </worldbody>
  17. </mujoco>
  18. """
  19. model = mujoco.MjModel.from_xml_string(xml)
  20. data = mujoco.MjData(model)
  21. renderer = mujoco.Renderer(model)
  22. # enable joint visualization option:
  23. scene_option = mujoco.MjvOption()
  24. scene_option.flags[mujoco.mjtVisFlag.mjVIS_JOINT] = True
  25. duration = 3.8 # (seconds)
  26. framerate = 60 # (Hz)
  27. frames = []
  28. # media.show_video(frames, fps=framerate)
  29. fourcc = cv2.VideoWriter_fourcc(*'XVID')
  30. #cap = cv2.VideoCapture(frames)
  31. size = (320,240)
  32. out = cv2.VideoWriter('out.avi',fourcc,framerate,size)
  33. mujoco.mj_resetData(model, data)
  34. while data.time < duration:
  35. mujoco.mj_step(model, data)
  36. if len(frames) < data.time * framerate:
  37. renderer.update_scene(data, scene_option=scene_option)
  38. pixels = renderer.render()
  39. frames.append(pixels)
  40. out.write(pixels)
  41. # Simulate and display video.

把输出的问题解决了,接下来就可以开始考虑硬件了。

什么是倒立摆?维基百科上是这样定义的:

https://zh.wikipedia.org/zh-cn/%E5%80%92%E5%96%AE%E6%93%BA

本质上,倒立摆是一个底部运动、上侧随动的结构。它能够通过二者的连接关节来测试倒立摆是否稳定。但我们只需要在mujoco里面搭建一个底部能动的模型,所以就差不多了。

大致结构如下:地面 - 滑动关节 - 横向移动模块 - 转动关节 - 立杆

因此,首先我需要写出来一个xml格式的结构。

  1. <mujoco>
  2. <worldbody>
  3. <light name="top" pos="0 0 2"/>
  4. <body name="inverted_pendulum">
  5. <body name="bottom">
  6. <joint name="swing" type="slide" axis="0 1 0" limited="true" range="-0.5 0.5" />
  7. <geom name="bottom" type="box" condim="1" size="0.2 0.2 0.2" rgba="0.4 0.4 0.4 1" />
  8. </body>
  9. <body name="upper">
  10. <joint name="rotate" type="hinge" axis="1 0 0" pos="0 0 0.2" />
  11. <geom name="upper" type="capsule" size="0.05 0.5" pos="0 0 0.7" rgba="0.75 0.75 0.5 1" />
  12. </body>
  13. </body>
  14. </worldbody>
  15. </mujoco>

设计之后,形状如下:

 接下来,给它添加actuator和initial,再将其转成动画。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/929758
推荐阅读
相关标签
  

闽ICP备14008679号