Always overshoots on heating?

First up, this isn’t really a complaint - I love my BrewPi and it works fantastically almost all of the time. However, I do have one persistent issue that puzzles me, which is that I always get a slight overshoot when heating (much less so on cooling).

This example image shows what I am talking about. This is just after pitching, and I am bringing the beer up to a constant 20C. What I can’t understand is why the heater stays on even after the fridge temp has reached the target - here you can see that it stays on for about another 15 minutes, which cases the beer to overshoot by about 0.5C. When cooling, it behaves much more like I would expect a PID to behave; it cools for a while, then shuts off even before the target is achieved and waits for the beer to catch up.
Is this fixable?

{
  "kind": "Control",
  "pids": [
    {
      "kind": "Pid",
      "name": "heater1",
      "enabled": true,
      "setPoint": {
        "kind": "SetPointSimple",
        "name": "fridgeset",
        "value": 7
      },
      "inputSensor": {
        "kind": "TempSensorFallback",
        "onBackupSensor": false,
        "sensor": {
          "kind": "TempSensor",
          "name": "fridge",
          "sensor": {
            "kind": "OneWireTempSensor",
            "value": 8.6875,
            "connected": true,
            "address": "284D14C207000006",
            "calibrationOffset": 0
          }
        }
      },
      "inputError": 2.4531,
      "Kp": 10,
      "Ti": 600,
      "Td": 60,
      "p": -24.5313,
      "i": 0,
      "d": 0.1953,
      "actuatorIsNegative": false,
      "outputActuator": {
        "kind": "ActuatorPwm",
        "value": 0,
        "period": 4,
        "minVal": 0,
        "maxVal": 100,
        "target": {
          "kind": "ActuatorMutexDriver",
          "mutexGroup": {
            "kind": "ActuatorMutexGroup",
            "deadTime": 1200000,
            "waitTime": 0
          },
          "target": {
            "kind": "ActuatorPin",
            "state": false,
            "pin": 16,
            "invert": false
          }
        }
      }
    },
    {
      "kind": "Pid",
      "name": "heater2",
      "enabled": true,
      "setPoint": {
        "kind": "SetPointSimple",
        "name": "beer2set",
        "value": null
      },
      "inputSensor": {
        "kind": "TempSensor",
        "name": "beer2",
        "sensor": {
          "kind": "OneWireTempSensor",
          "value": 11.625,
          "connected": true,
          "address": "285B4FC107000082",
          "calibrationOffset": 0
        }
      },
      "inputError": null,
      "Kp": 10,
      "Ti": 600,
      "Td": 60,
      "p": 0,
      "i": 0,
      "d": 0,
      "actuatorIsNegative": false,
      "outputActuator": {
        "kind": "ActuatorPwm",
        "value": 0,
        "period": 4,
        "minVal": 0,
        "maxVal": 100,
        "target": {
          "kind": "ActuatorMutexDriver",
          "mutexGroup": {
            "kind": "ActuatorMutexGroup",
            "deadTime": 1200000,
            "waitTime": 0
          },
          "target": {
            "kind": "ActuatorNop",
            "state": false
          }
        }
      }
    },
    {
      "kind": "Pid",
      "name": "cooler",
      "enabled": true,
      "setPoint": {
        "kind": "SetPointSimple",
        "name": "fridgeset",
        "value": 7
      },
      "inputSensor": {
        "kind": "TempSensorFallback",
        "onBackupSensor": false,
        "sensor": {
          "kind": "TempSensor",
          "name": "fridge",
          "sensor": {
            "kind": "OneWireTempSensor",
            "value": 8.6875,
            "connected": true,
            "address": "284D14C207000006",
            "calibrationOffset": 0
          }
        }
      },
      "inputError": 2.4531,
      "Kp": 10,
      "Ti": 1800,
      "Td": 200,
      "p": -24.5313,
      "i": 0,
      "d": 0.6641,
      "actuatorIsNegative": true,
      "outputActuator": {
        "kind": "ActuatorPwm",
        "value": 23.8672,
        "period": 1200,
        "minVal": 0,
        "maxVal": 100,
        "target": {
          "kind": "ActuatorMutexDriver",
          "mutexGroup": {
            "kind": "ActuatorMutexGroup",
            "deadTime": 1200000,
            "waitTime": 0
          },
          "target": {
            "kind": "ActuatorTimeLimited",
            "minOnTime": 0,
            "minOffTime": 180,
            "maxOnTime": 65535,
            "state": false,
            "target": {
              "kind": "ActuatorPin",
              "state": false,
              "pin": 17,
              "invert": false
            }
          }
        }
      }
    },
    {
      "kind": "Pid",
      "name": "beer2fridge",
      "enabled": false,
      "setPoint": {
        "kind": "SetPointSimple",
        "name": "beer1set",
        "value": null
      },
      "inputSensor": {
        "kind": "TempSensor",
        "name": "beer1",
        "sensor": {
          "kind": "OneWireTempSensor",
          "value": 8.625,
          "connected": true,
          "address": "28E36AC207000086",
          "calibrationOffset": 0
        }
      },
      "inputError": null,
      "Kp": 10,
      "Ti": 15000,
      "Td": 0,
      "p": 0,
      "i": 0,
      "d": 0,
      "actuatorIsNegative": false,
      "outputActuator": {
        "kind": "ActuatorSetPoint",
        "targetSetPoint": {
          "kind": "SetPointSimple",
          "name": "fridgeset",
          "value": 7
        },
        "targetSensor": {
          "kind": "TempSensor",
          "name": "fridge",
          "sensor": {
            "kind": "OneWireTempSensor",
            "value": 8.6875,
            "connected": true,
            "address": "284D14C207000006",
            "calibrationOffset": 0
          }
        },
        "referenceSetPoint": {
          "kind": "SetPointSimple",
          "name": "beer1set",
          "value": null
        },
        "output": 127.9961,
        "achieved": 127.9961,
        "minimum": -10,
        "maximum": 10
      }
    }
  ]
}

I think this effect might be reduced in version 0.4.4. There are some small changes to how the integrator anti-windup is calculated.

Which version did you run here?

What also could help is increasing Td on the heater.

Next time this happens, copy the output on the control algorithm page. (at around 15:35)