You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Meshcat接触可视化工具删除后无法重绘接触力问题咨询

问题背景

我有一个包含图(diagram)及其上下文(context)的TrajectoryVisualizer对象,用于可视化轨迹及轨迹上的接触力,代码如下:

class TrajectoryVisualizer:
    def __init__(self,):
        self.diagram = make_diagram_with_plant_and_scene_graph_and_visualizer_and_contact_visualizer() 
        self.meshcat # Meshcat object
        self.contact_vis  # ContactVisualizer object
        self.meshcat_vis  # MeshcatVisualizer object
        self.plant  # MultibodyPlant object

        self.context = self.diagram.CreateDefaultContext()
        self.context_meshcat = self.meshcat_vis.GetMyMutableContextFromRoot(
            self.context
        )
        self.context_contact_vis = (
            self.contact_vis.GetMyMutableContextFromRoot(self.context)
        )
        self.context_plant = self.plant.GetMyMutableContextFromRoot(self.context)

    def publish_trajectory(
        self,
        h: float,
        q_knots: np.ndarray,
        contact_results_list: List[ContactResults],
    ):
        """
        q_knots: (T + 1, n_q) array.
        contact_results_list: (T,) list.
        For 1 <= i <= T, contact_results_list[i - 1] is the contact forces at
         configuration q_knots[i].
        """
        assert len(q_knots) == len(contact_results_list) + 1


        self.meshcat_vis.DeleteRecording()
        self.meshcat_vis.StartRecording(False)

        for i, t in enumerate(np.arange(len(q_knots)) * h):
            self.context.SetTime(t)
            self.plant.SetPositions(self.context_plant)
            self.meshcat_vis.ForcedPublish(self.context_meshcat)

            if i > 0:
                # Contact forces
                self.contact_vis.GetInputPort("contact_results").FixValue(
                    self.context_contact_vis,
                    AbstractValue.Make(contact_results_list[i - 1]),
                )
                self.contact_vis.ForcedPublish(self.context_contact_vis)

        self.meshcat_vis.StopRecording()
        self.meshcat_vis.PublishRecording()

但发现删除接触力后,再次绘制相同轨迹时接触力不再显示:

vis = TrajectoryVisualizer()
vis.publish_trajectory(h, q_knots, contact_results_list)  # 首次绘制正常
vis.meshcat.Delete("contact_forces")
vis.publish_trajectory(h, q_knots, contact_results_list)  # 接触力不再显示

我有两个问题:

  1. 该故障是否由缓存导致?若是,如何修复?
  2. 可行方案是每次调用vis.publish_trajectory(...)时创建新Context,但我认为Context构造成本较高,因此仅在构造函数中创建一次,该理解是否正确?

我认为创建新Context可解决问题,但希望找到最高效的可视化方式。


解答

问题1:故障原因与修复方案

这个故障不是由Context缓存导致,而是因为调用vis.meshcat.Delete("contact_forces")直接删除了Meshcat中承载接触力的节点结构,而ContactVisualizer默认不会自动重建已被删除的Meshcat节点。

修复方案有两种:

  • 方案一:重新初始化ContactVisualizer节点
    在每次发布轨迹前调用contact_vis.Reinitialize(),它会重新创建contact_forces节点及子结构。修改publish_trajectory方法:
    def publish_trajectory(...):
        # 新增初始化逻辑
        self.contact_vis.Reinitialize()
        self.meshcat_vis.DeleteRecording()
        # 后续原有代码不变
    
  • 方案二:隐藏而非删除节点
    替代vis.meshcat.Delete("contact_forces"),使用vis.meshcat.SetProperty("contact_forces", "visible", False)隐藏接触力;需要显示时再设为True,这样节点结构不会被破坏,后续发布轨迹时能正常更新显示。

问题2:Context创建成本的理解与高效方案

你的理解是正确的:Diagram.CreateDefaultContext()的成本确实较高,因为它需要为整个图的所有组件(包括Plant、Visualizer等)分配并初始化上下文状态,频繁创建会带来不必要的性能开销,因此在构造函数中创建一次并复用是更高效的选择。

不需要每次发布轨迹都创建新Context,问题1的修复方案已经能在复用Context的前提下解决接触力不显示的问题。如果后续遇到其他Context状态污染问题,也可以通过重置Context到默认状态替代创建新Context,开销远低于新建操作:

# 在publish_trajectory开头添加重置逻辑
self.diagram.SetDefaultContext(self.context)
# 重新获取子组件的上下文引用(可选,若之前的引用未失效)
self.context_meshcat = self.meshcat_vis.GetMyMutableContextFromRoot(self.context)
self.context_contact_vis = self.contact_vis.GetMyMutableContextFromRoot(self.context)
self.context_plant = self.plant.GetMyMutableContextFromRoot(self.context)

内容的提问来源于stack exchange,提问作者Pang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:10:34