Road painting ahead
Ruby, 245
Print the lane divides if relevant, then print the lane.
I don't expect to win.
->(n,i){i.times{|d,t|*e=''
g=e+%w{HOV3 ^^ B}
n.chars{|c|$><<(c==?M?'##':!t ??!:(t+c)[?H]&&d%10<5??x:?|)if(M=t!=?M)
$><<((e+[(%w{/\\ <- ->}+g)[v='TLRUHPB'.index(c)],(%w{\\/ \ | |\ }+g)[v]]+e*4)*2)[d%10].center(v>5?3:6)if(t=c)!=?M}
puts M ? e:?!}}
Changelog
245 choke stderr and split arrays effectively.
263 better way to index array
268 just print each line, don't calculate a canonical version.
330 initial commit
JavaScript (ES6), 316 bytes
f=(x,n)=>{for(i=0;n>i++;){b=!(r=i%10)|r==3;y=[...`! ${[...x].join` | `} !`[o='replace'](/[\W] ?M [\W]?/g,'##')].map(c=>~(q='LPRTU'.indexOf(c))?` ${'<- |^^^^->| /\\\\/ '.substr(4*q+2*b,2)} `:c=='H'?'HOV3':c).join``;y=r&&r<6?y[o](/\| H/g,'x H')[o](/3 \|/g,'3 x'):y;console.log(b|r==2|r==9?y:y[o](/[^!\|x#]/g,' '))}}
Demo
It should work in Firefox and Edge at time of writing, Chrome/Opera require experimental features to be enabled.
console.log = x => O.innerHTML += x + '\n';
f = (x, n) => {
for (i = 0; n > i++;) {
b = !(r = i % 10) | r == 3;
y = [...
`! ${[...x].join` | `} !` [o = 'replace'](/[\W] ?M [\W]?/g, '##')
].map(c => ~(q = 'LPRTU'.indexOf(c)) ? ` ${'<- |^^^^->| /\\\\/ '.substr(4*q+2*b,2)} ` : c == 'H' ? 'HOV3' : c).join ``;
y = r && r < 6 ? y[o](/\| H/g, 'x H')[o](/3 \|/g, '3 x') : y;
console.log(b | r == 2 | r == 9 ? y : y[o](/[^!\|x#]/g, ' '))
}
}
// Snippet stuff
var demo = () => {
O.innerHTML = '';
document.forms[0].checkValidity() && f(document.getElementById('P').value, document.getElementById('N').valueAsNumber);
};
document.getElementById('P').addEventListener('change', demo);
document.getElementById('N').addEventListener('change', demo);
demo();
<form action='#'>
<p>
<label>Lane pattern:
<input type=text pattern=^M?([BHLPRTU]M?)+$ maxlength=19 required id=P value=MLTPUMHUTBR>
</label>
</p>
<p>
<label>Kilometres:
<input type=number id=N min=1 value=21 max=50 step=1 required>
</label>
</p>
<pre><output id=O></output></pre>
</form>