[SOLVED] Prismatic joint collapses when told to move backwards, ever, for any amount. Behaves correctly forwards

Hi everyone, bizarre issue that I would love to get some help with as I am stuck. I am trying to use a prismatic joint to move a platform up and down vertically. When commanded to go up, it behaves as intended, and moves to the correct position. However, if given a command to go down ever, for example to 0.4m from 0.5m it simply collapses to the minimum value specified, and then is unresponsive to any further commands. The full .sdf file I made to test this is here:

<?xml version="1.0" ?>
<sdf version="1.6">
  <world name="default">
    <scene>
      <ambient>0.4 0.4 0.4</ambient>
      <grid>false</grid>
    </scene>

    <!--              -->
    <!-- Illumination -->
    <!--              -->

    <light type="directional" name="sun">
      <cast_shadows>false</cast_shadows>
      <pose>5 5 5 0 0 0</pose>
      <diffuse>0.8 0.8 0.8 1</diffuse>
      <specular>0.2 0.2 0.2 1</specular>
      <attenuation>
        <range>1000</range>
        <constant>0.9</constant>
        <linear>0.01</linear>
        <quadratic>0.001</quadratic>
      </attenuation>
      <direction>-1 -1 -1</direction>
    </light>
 
    <!--        -->
    <!-- Models -->
    <!--        -->

    <model name="barge">
      <pose>0 0 0 0 0 0</pose>

      <!-- Fix To World -->
      <joint name="foundation" type="fixed">
        <parent>world</parent>
        <child>root</child>
      </joint>

      <!-- LINKS  -->

      <link name="root">
        <visual name="root_debug_visual">
          <geometry>
            <sphere>
              <radius>0.025</radius>
            </sphere>
          </geometry>
          <material>
            <ambient>0 0.5 0.5 1</ambient>
            <diffuse>0 0.8 0.8 1</diffuse>
            <specular>0.8 0.8 0.8 1</specular>
          </material>
        </visual>
      </link>

      <link name='base'>
        <pose relative_to='root'>0 0 0 0 0 0</pose>
        <visual name='base_visual'>
          <geometry>
            <box>
              <size>0.5 0.5 0.05</size>
            </box>
          </geometry>
          <material>
            <diffuse>1 1 1 1</diffuse>
            <specular>0.4 0.4 0.4 1</specular>
          </material>
        </visual>
        <collision name='base_collision'>
          <geometry>
            <box>
              <size>0.5 0.5 0.05</size>
            </box>
          </geometry>
          <surface>
            <friction>
              <ode>
                <mu>1.0</mu>
                <mu2>1.0</mu2>
              </ode>
            </friction>
            <bounce>
              <restitution_coefficient>0.1</restitution_coefficient>
              <threshold>1.0</threshold>
            </bounce>
          </surface>
        </collision>
      </link>

      <!-- JOINTS -->
      <joint name="heave" type="prismatic">
        <parent>root</parent>
        <child>base</child>
        <pose>0 0 0 0 0 0</pose>
        <axis>
          <xyz>0 0 1</xyz>
          <limit>
            <lower>-2</lower>
            <upper>2</upper>
            <velocity>1</velocity>
            <effort>500</effort>
          </limit>
          <dynamics>
            <damping>3</damping>
          </dynamics>
        </axis>
      </joint>

      <!-- PLUGINS -->
    
      <plugin
        filename="gz-sim-joint-position-controller-system"
        name="gz::sim::systems::JointPositionController">
        <joint_name>heave</joint_name>
        <use_velocity_commands>true</use_velocity_commands>
        <topic>heave_ctrl</topic>
      </plugin>

      <plugin
        filename="gz-sim-joint-state-publisher-system"
        name="gz::sim::systems::JointStatePublisher">
        <joint_name>heave</joint_name>
      </plugin>

    </model>
  </world>
</sdf>

And this can be controlled with a command such as this:
gz topic -t “/heave_ctrl” -m gz.msgs.Double -p “data: 0.5”

This command would work, but then sending something like:
gz topic -t “/heave_ctrl” -m gz.msgs.Double -p “data: 0.3”

Would cause the joint to collapse to -2m and then stay there indefinitely. I have no idea what could be going on! I am using Ubuntu 22.04 with Gazebo Harmonic 8.9.0.

What is even stranger is that it is Z axis specific - if I set the prismatic joint to X or Y it works fine, and if I rotate the model so that Z is aligned with the world’s X axis, again it works fine. But at 45 degrees, for example, it collapses again. Maybe something to do with gravity?

Confirmed an issue in Gazebo Ionic 9.1.0 as well. No closer to any idea why this is happening.

[SOLVED] Prismatic joint always goes to minimum position when given a lower position in Z than its current position. · Issue #2785 · gazebosim/gz-sim · GitHub
Bug with DART implementation. Remove neg velocity limit and it behaves as normal.