{"action":"created","issue":{"url":"https://api.github.com/repos/ev3dev/ev3dev/issues/853","repository_url":"https://api.github.com/repos/ev3dev/ev3dev","labels_url":"https://api.github.com/repos/ev3dev/ev3dev/issues/853/labels{/name}","comments_url":"https://api.github.com/repos/ev3dev/ev3dev/issues/853/comments","events_url":"https://api.github.com/repos/ev3dev/ev3dev/issues/853/events","html_url":"https://github.com/ev3dev/ev3dev/issues/853","id":210920414,"node_id":"MDU6SXNzdWUyMTA5MjA0MTQ=","number":853,"title":"Motor synchronisation for constant speed ratio","user":{"login":"ndward","id":9800891,"node_id":"MDQ6VXNlcjk4MDA4OTE=","avatar_url":"https://avatars1.githubusercontent.com/u/9800891?v=4","gravatar_id":"","url":"https://api.github.com/users/ndward","html_url":"https://github.com/ndward","followers_url":"https://api.github.com/users/ndward/followers","following_url":"https://api.github.com/users/ndward/following{/other_user}","gists_url":"https://api.github.com/users/ndward/gists{/gist_id}","starred_url":"https://api.github.com/users/ndward/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ndward/subscriptions","organizations_url":"https://api.github.com/users/ndward/orgs","repos_url":"https://api.github.com/users/ndward/repos","events_url":"https://api.github.com/users/ndward/events{/privacy}","received_events_url":"https://api.github.com/users/ndward/received_events","type":"User","site_admin":false},"labels":[{"id":58783639,"node_id":"MDU6TGFiZWw1ODc4MzYzOQ==","url":"https://api.github.com/repos/ev3dev/ev3dev/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":125899385,"node_id":"MDU6TGFiZWwxMjU4OTkzODU=","url":"https://api.github.com/repos/ev3dev/ev3dev/labels/motors","name":"motors","color":"0052cc","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":19,"created_at":"2017-02-28T22:24:37Z","updated_at":"2018-06-19T01:53:34Z","closed_at":null,"author_association":"NONE","body":"This message will make the case that **EV3dev badly needs a function that synchronises two motor motions such that the two motors have a constant and correct speed ratio**, thereby also causing the stopping of the motors to be synchronised. This should not be confused with the synchronised _starting_ of motor movements which was the subject of issue 454. Also, motor _regulation_ should not be confused with the _synchronisation_ of two motors. \r\n\r\nMost EV3-compatible languages (EV3-G, RobotC, EV3 Basic, LeJos…) have a function or multiple functions that offer motor synchronisation and it works very well. The motors turn with the requested speed ratio, turn through the correct angles and stop simultaneously. But because EV3dev/EV3 Python does not have such a feature it is, I believe, impossible or very difficult to achieve these results (correct speed ratio, correct angles, simultaneous stop). That's because **individually-controlled EV3 motors usually do not turn with the requested speed**. This was discussed in depth in EV3 Python issue 220 https://github.com/rhempel/ev3dev-lang-python/issues/220 . Summarising that issue, **EV3 motors tend to turn slower than requested for high values of speed_sp** (assuming starting from a standstill, in which case the inertia of the motor stops the motor from having the requested speed at the beginning of this motion). **For low values of speed_sp the EV3 motors tend to turn faster than requested**. This is to do with the difficulty of measuring slow speeds accurately over very small time intervals, though that does not fully explain why there is a systematic tendency for the motors to turn faster than requested. These discrepancies can be very significant. A table in issue 220 shows that for a requested speed_sp value of 50 the actual speed can be more than 20% greater than requested. These discrepancies are due to the properties of the motors themselves and are not EV3dev specific so I would expect other languages to have the same discrepancies but in other languages it would be less of a problem because motor synchronisation allows for the motors to move together with the correct speed ratio through the correct angles.\r\n\r\nRover robots are a classic application of the EV3 and it's vital to be able to make the rover follow a curved path accurately. Other languages can do this thanks to motor synchronisation but I believe EV3dev cannot. It would be banal to configure the move tank block in EV3-G, for example, to make motorB turn with speed 80 for 5 rotations while motorC turns at speed 20, stopping at the same time as motorB . Also, the block would usually be configured to make the program pause until the motors have completed their motions. In EV3 Python the equivalent code might look like this:\r\n\r\n```\r\n#!/usr/bin/env python3\r\nfrom ev3dev.ev3 import *\r\nmB = LargeMotor('outB')\r\nmC = LargeMotor('outC')\r\nmB.run_to_rel_pos(position_sp=1800, speed_sp=800, stop_action='hold')\r\nmC.run_forever(speed_sp=200, stop_action='hold')\r\nmB.wait_while('running')\r\nmC.stop()\r\n```\r\n\r\nThis code stops the slower motor once the faster motor has finished its movement, which is the behaviour described by Lego for the move tank block in the official documentation. Modifying the code slightly we can see how much each motor rotated:\r\n```\r\n#!/usr/bin/env python3\r\nfrom ev3dev.ev3 import *\r\nmB = LargeMotor('outB')\r\nmC = LargeMotor('outC')\r\nmB.position=0\r\nmC.position=0\r\nmB.run_to_rel_pos(position_sp=1800, speed_sp=800, stop_action='hold')\r\nmC.run_forever(speed_sp=200, stop_action='hold')\r\nmB.wait_while('running')\r\nmC.stop()\r\nprint(mB.position)\r\nprint(mC.position)\r\n```\r\n\r\nSince motor C is set to turn at a quarter of the speed of motor B and both motors will turn for the same time we are implicitly requesting that motor C turns through a quarter of the angle turned through by motor B, i.e. 450°. In fact my tests show that motor C (starting from rest and with almost no load) turns through about 488°, or about 8% more than requested. This discrepancy is consistent with the observations raised in issue 220, that for small values of speed_sp the motor tends to turn faster than requested. Unlike the EV3 Python script above, the EV3-G move tank block turns the motors through the CORRECT angles because the motors are SYNCHRONISED to maintain the correct speed ratio. Therefore EV3 Python seems to be unable to match the precision of EV3-G in this case due to the impossibility of using synchronised motor movements.\r\n\r\nYou may be wondering whether a better result could be obtained with this code:\r\n```\r\n#!/usr/bin/env python3\r\nfrom ev3dev.ev3 import *\r\nmB = LargeMotor('outB')\r\nmC = LargeMotor('outC')\r\nmB.run_to_rel_pos(position_sp=1800, speed_sp=800, stop_action='hold')\r\nmC.run_to_rel_pos(position_sp=450, speed_sp=200, stop_action='hold')\r\nmB.wait_while('running')\r\nmC.wait_while('running')\r\n```\r\n\r\nThis code will indeed give correct results for the angles turned by each motor but there is still a problem: the slower motor turns faster than requested and the faster motor turns slower than requested (in accordance with issue 220) and therefore the slower motor completes its motion slightly before the faster motor (if you cannot see this effect easily then change the angles in the above code to 18000 and 4500). Therefore the movement happens in two stages: first the two motors are turning but the rover robot is turning less sharply than requested, then motor C stops turning and motor B continues turning for a moment while the rover turns more sharply than requested. This complex and undesired behaviour could be avoided if the motors could be synchronised to maintain a constant and correct speed ratio.\r\n\r\nThere is another good reason why it would be great to have a synchronised motor function in EV3dev/EV3 Python: it would allow for more concise code. Instead of needing two lines to launch the two motors only one would be needed. And a keyword argument could be included to make the program wait (or not) for the motors to complete their motion before continuing, further shortening the code. Note that the motion described in this message can be achieved in just one line in EV3 Basic :\r\n`Motor.MoveSync(\"BC\", 80, 20, 1800, \"True\")`\r\n(This means: move motors B and C synchronously with speed B=80, speed C=20, with the faster motor turning through 1800° and the brake applied when the motion completes.)\r\n\r\nBut we have seen that achieving roughly the same (with less precision!) requires eight lines in EV3 Python. Python has a reputation for being relatively concise so it is disturbing to see that it takes eight times as many lines to achieve the same task compared to EV3 Basic (see my site ev3basic.com). I understand that it is rather difficult to write code for the synchronisation of motors but the developer of EV3 Basic, Reinhard Grafl, was able to write such a function working alone so I am confident that the EV3dev team can manage the same!"},"comment":{"url":"https://api.github.com/repos/ev3dev/ev3dev/issues/comments/398249201","html_url":"https://github.com/ev3dev/ev3dev/issues/853#issuecomment-398249201","issue_url":"https://api.github.com/repos/ev3dev/ev3dev/issues/853","id":398249201,"node_id":"MDEyOklzc3VlQ29tbWVudDM5ODI0OTIwMQ==","user":{"login":"dlech","id":963645,"node_id":"MDQ6VXNlcjk2MzY0NQ==","avatar_url":"https://avatars0.githubusercontent.com/u/963645?v=4","gravatar_id":"","url":"https://api.github.com/users/dlech","html_url":"https://github.com/dlech","followers_url":"https://api.github.com/users/dlech/followers","following_url":"https://api.github.com/users/dlech/following{/other_user}","gists_url":"https://api.github.com/users/dlech/gists{/gist_id}","starred_url":"https://api.github.com/users/dlech/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dlech/subscriptions","organizations_url":"https://api.github.com/users/dlech/orgs","repos_url":"https://api.github.com/users/dlech/repos","events_url":"https://api.github.com/users/dlech/events{/privacy}","received_events_url":"https://api.github.com/users/dlech/received_events","type":"User","site_admin":false},"created_at":"2018-06-19T01:53:34Z","updated_at":"2018-06-19T01:53:34Z","author_association":"MEMBER","body":"> Well, I'm curious enough to give it a stab.\r\n> Just monitoring the quadrature encoders sounds doable for now.\r\n\r\nGreat. If you give me a few days, I'll try to clean up the PRU UART code and create a new driver template for loading your firmware in the other PRU.\r\n\r\n> However, I'm not sure how to arbitrate access to the quadrature encoder GPIOs between this and the tacho motor driver. Would it be ok to just poll them, then I don't have to worry about interrupt handlers which are probably being used by the regular tacho motor driver currently?\r\n\r\nThe PRUs have their own \"interrupt controllers\". I put this in quotation marks because \"interrupts\" don't actually trigger an interrupt handler like you may be accustomed to. So, you basically have to poll the GPIOs even if you do use \"interrupts\". And, you don't have to worry about affecting the ARM interrupt controller.\r\n\r\nGiven that and the fact that we are using these GPIOs as inputs, you don't have to worry about conflicting with the current motor driver.\r\n\r\n> Should I do this using the ev3dev-pru-firmware repo?\r\n\r\nThat would be the best place to submit PRU code.\r\n\r\n> Finally what version of CSS should I use? The latest release is version 8.\r\n\r\nLatest version is probably the best. I haven't touched it in a while.\r\n\r\n"}}
{"actor":{"display_login":"dlech"}}