shadow-robot / optoforce

ROS driver for the Optoforce sensor
GNU General Public License v2.0
21 stars 17 forks source link

Serial number #20

Closed dogoepp closed 8 years ago

dogoepp commented 8 years ago

Since we have six sensors on one robot, we need to have a way to uniquely identify them. Using the name that the OS gives them is not reliable enough since it depends on the order in which they are connected.

I propose to add the option for the topic name to contain the sensor's serial number.

A number of other changes were made, hopefully for the better.

Changes:

kirstyellis commented 8 years ago

hi @dogoepp, we also have had the issue of multiple sensors and the order they are connected being a problem. The way we overcame this was instead of using port in the form of '/dev/ttyACM0' we used the value '/dev/serial/by-path/UNIQUE_IDENTIFIER' or '/dev/serial/by-id/UNIQUE_IDENTIFIER' as these values (symlinks to /dev/ACM0 path) are unique for each sensor. Have you considered this solution?

dogoepp commented 8 years ago

I don't have any /dev/serial folder on our computer. We (still) use Ubuntu 14.04 and the USB based 1 sensor DAQs.

kirstyellis commented 8 years ago

hmm, that is strange. We are also using Ubuntu and 14.04 and USB based 1 sensor DAQs. The directory only exists if you have a serial device connected to your machine. If you can please confirm that you have checked for /dev/serial when a serial device is connected then I will take a look at your PR. Thanks!

dogoepp commented 8 years ago

What do you call a serial device ? I have the six sensors connected when I check the existence of this /dev/serial folder. I didn't find it anyway.

May it be because the serial interface is emulated through a modem connexion (ttyACM) ?

kirstyellis commented 8 years ago

would you be able to provide your dmesg logs of when you attach a sensor, as well as the output of lsusb and run: ll /lib/udev/rules.d/??-persistent-serial.rules And provide me with this output also. Thanks

dogoepp commented 8 years ago

The output of ldusb is

Bus 002 Device 002: ID 8087:8002 Intel Corp. 
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:800a Intel Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 005: ID 0451:8041 Texas Instruments, Inc. 
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 121: ID 413c:2105 Dell Computer Corp. Model L100 Keyboard
Bus 003 Device 120: ID 046d:c077 Logitech, Inc. M105 Optical Mouse
Bus 003 Device 119: ID 0451:8043 Texas Instruments, Inc. 
Bus 003 Device 040: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo
Bus 003 Device 039: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo
Bus 003 Device 038: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo
Bus 003 Device 037: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 003 Device 036: ID 0409:005a NEC Corp. HighSpeed Hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Where the Microchip Technology, Inc. CDC RS-232 Emulation Demo are the three connected sensors.

The output of dmesg is too long. I attach it as a file: dmesg.txt

kirstyellis commented 8 years ago

Brilliant, thank you for that, could I also get the output of: ll /lib/udev/rules.d/??-persistent-serial.rules

it is this udev rule that creates the serial directory when the device is connected.

dogoepp commented 8 years ago

The path on our computer is resolved to /lib/udev/rules.d/60-persistent-serial.rules and its content is

# do not edit this file, it will be overwritten on update

ACTION=="remove", GOTO="persistent_serial_end"
SUBSYSTEM!="tty", GOTO="persistent_serial_end"
KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="persistent_serial_end"

SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}"

IMPORT{builtin}="path_id"
ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}"
ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}"

IMPORT{builtin}="usb_id"
ENV{ID_SERIAL}=="", GOTO="persistent_serial_end"
SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}"
ENV{ID_USB_INTERFACE_NUM}=="", GOTO="persistent_serial_end"
ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}"
ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"

LABEL="persistent_serial_end"
kirstyellis commented 8 years ago

That is the same as on both my machine and my colleague's. Could you unplug the device, run: udevadm monitor --udev then reconnect the device and paste the output here. Also check for /dev/serial. If no /dev/serial, try a restart of your machine and again run the command during a reconnect of the device. Thanks

dogoepp commented 8 years ago

The output of udevadm monitor --udev when connecting a hub with three sensors is

monitor will print the received events for:
UDEV - the event which udev sends out after rule processing

UDEV  [57592.314829] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)
UDEV  [57592.316146] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
UDEV  [57592.706425] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4 (usb)
UDEV  [57592.707579] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4:1.0 (usb)
UDEV  [57593.099324] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1 (usb)
UDEV  [57593.100986] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1/3-1.4.1:1.0 (usb)
UDEV  [57593.101664] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1/3-1.4.1:1.1 (usb)
UDEV  [57593.102626] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1/3-1.4.1:1.0/tty/ttyACM0 (tty)
UDEV  [57593.303327] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2 (usb)
UDEV  [57593.304554] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2/3-1.4.2:1.1 (usb)
UDEV  [57593.304585] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2/3-1.4.2:1.0 (usb)
UDEV  [57593.305736] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2/3-1.4.2:1.0/tty/ttyACM1 (tty)
UDEV  [57593.507605] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3 (usb)
UDEV  [57593.508791] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3/3-1.4.3:1.0 (usb)
UDEV  [57593.508970] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3/3-1.4.3:1.1 (usb)
UDEV  [57593.509995] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3/3-1.4.3:1.0/tty/ttyACM2 (tty)
UDEV  [57593.672058] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4 (usb)
UDEV  [57593.673950] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4/3-1.4.4:1.1 (usb)
UDEV  [57593.673981] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4/3-1.4.4:1.0 (usb)
UDEV  [57593.675348] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4/3-1.4.4:1.0/tty/ttyACM3 (tty)

But, my bad, we had an udev rule that seemed to override the creation of the serial folder. Nonetheless, only one item is created for the optoforce sensor in /dev/serial/by-id:

lrwxrwxrwx 1 root root 13 Aug 10 08:03 usb-OptoForce_OptoForce_DAQ-if00 -> ../../ttyACM2
lrwxrwxrwx 1 root root 13 Aug 10 08:03 usb-Xevelabs_USB2AX_74035303530351417142-if00 -> ../../ttyACM3

the usb-Xevelabs is an USB to dynamixel interface plugged on the same hub as the sensors.

After reboot, the output of udevadm became

UDEV  [52.154572] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1 (usb)
UDEV  [52.155291] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0 (usb)
UDEV  [52.550070] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4 (usb)
UDEV  [52.550777] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4:1.0 (usb)
UDEV  [52.939922] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1 (usb)
UDEV  [52.941328] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1/3-1.4.1:1.1 (usb)
UDEV  [52.943028] add      /module/cdc_acm (module)
UDEV  [52.944952] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1/3-1.4.1:1.0 (usb)
UDEV  [52.944964] add      /bus/usb/drivers/cdc_acm (drivers)
UDEV  [52.945604] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.1/3-1.4.1:1.0/tty/ttyACM0 (tty)
UDEV  [53.129300] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2 (usb)
UDEV  [53.129871] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2/3-1.4.2:1.0 (usb)
UDEV  [53.129983] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2/3-1.4.2:1.1 (usb)
UDEV  [53.130559] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.2/3-1.4.2:1.0/tty/ttyACM1 (tty)
UDEV  [53.348818] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3 (usb)
UDEV  [53.349491] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3/3-1.4.3:1.0 (usb)
UDEV  [53.349524] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3/3-1.4.3:1.1 (usb)
UDEV  [53.350375] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.3/3-1.4.3:1.0/tty/ttyACM2 (tty)
UDEV  [53.530412] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4 (usb)
UDEV  [53.531492] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4/3-1.4.4:1.1 (usb)
UDEV  [53.531507] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4/3-1.4.4:1.0 (usb)
UDEV  [53.532274] add      /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.4/3-1.4.4/3-1.4.4:1.0/tty/ttyACM3 (tty)

but the content of /dev/serial/by-id is still the same. Only one instance for the optoforce sensor where I would need three. We had tried to discriminate the sensors with udev but the (USB-provided) serial numbers are all the same. I guess it can't do better than use the USB-provided data.

kirstyellis commented 8 years ago

Do you have the directory /dev/serial/by-path? It was the symlinks in this directory which we used as we also only had one instance in by-id

kirstyellis commented 8 years ago

Also, briefly looking at your PR, I am unsure as to how this helps with the issue with having to plug the DAQs in the correct order as you are still assigning a port in the form /dev/ttyACMx Are you using all single channel DAQs? We have been using 1 multi and one single channel, so we need to know port corresponds to each. A snippet from one of our launch files: `

`
dogoepp commented 8 years ago

The idea here is to get the serial number from the sensor by sending it a request. This part of the protocol is not in the official documentation but Optoforce was kind enough to tell me what to send and to receive. Then, we publish the wrench messages on a topic which is named with the serial number. For instance, on the single channel, single axis sensor ISE174, the topic published would be /optoforce_ISE174_0 (for starting_index = 0).

We connect to any optoforce serial interface available thanks to a udev rule that differentiates optoforce from any other ttyACM, but can't uniquely identify them. It is then the node's role to tell which sensor it is connected to.

I'm not very happy with this approach and better ideas are welcome.

----- Mail original -----

De: "kirstyellis" notifications@github.com À: "shadow-robot/optoforce" optoforce@noreply.github.com Cc: "Dorian Goepp" dorian.goepp@inria.fr, "Mention" mention@noreply.github.com Envoyé: Mercredi 10 Août 2016 12:50:35 Objet: Re: [shadow-robot/optoforce] Serial number (#20)

Also, briefly looking at your PR, I am unsure as to how this helps with the issue with having to plug the DAQs in the correct order as you are still assigning a port in the form /dev/ttyACMx Are you using all single channel DAQs? We have been using 1 multi and one single channel, so we need to know port corresponds to each. A snippet from one of our launch files: <

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub , or mute the thread .

dogoepp commented 8 years ago

Do you have the directory /dev/serial/by-path? It was the symlinks in this directory which we used as we also only had one instance in by-id

I'm afraid that the behavior is undetermined in this case. If I remember correctly, the path changes depending on which device is connected first and if they are disconnected and connected again.

----- Mail original -----

De: "kirstyellis" notifications@github.com À: "shadow-robot/optoforce" optoforce@noreply.github.com Cc: "Dorian Goepp" dorian.goepp@inria.fr, "Mention" mention@noreply.github.com Envoyé: Mercredi 10 Août 2016 12:05:21 Objet: Re: [shadow-robot/optoforce] Serial number (#20)

Do you have the directory /dev/serial/by-path? It was the symlinks in this directory which we used as we also only had one instance in by-id

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub , or mute the thread .

kirstyellis commented 8 years ago

Would you be able to provide more details about the udev rule that you are referring to.

I am still not clear on how your method would work in the scenario that we have here, in our launch file where we are launching 2 instances of optoforce.launch, 1 single axis and one multi, what port names should we put in the launch file? As you say, these change based on the order of which is plugged in first so if I launch port /dev/ttyACM0 as a multichannel DAQ then unplug and it is now /dev/ttyACM1, what will happen?

We have been using the by-path, it isn't as solid as by-id as you mentioned but we found that it wasn't affected by the order that the devices were plugged in and so far the values assigned to the ports haven't changed apart from one occasion. It isn't ideal!

dogoepp commented 8 years ago

We set two rules in /etc/udev/rules.d/:

You have quite a point. Since I still have to tell the nodes which port to use, my approach is clearly not generic enough. It wouldn't handle your use case correctly. I my case, I have six sensors of the same type, which made it less of an issue (but I realise now that I had ignored that the scaling factors are specific to each sensor).

I could consider having each node scan through the ttyACM recognized to be DAQs and stick with the one having the right serial number. Only, having several programs (or nodes) attempting to access the same files almost at the same time might cause headackes.

Maybe an other approach could be to have a "detector" node that probes each connected DAQ for the serial number and the publishes on a topic the map between the serial number and software path. In this case, the optoforce nodes would wait for the "detector" node to publish information on the serial number they are looking for.

dogoepp commented 8 years ago

I unconsciously excluded the idea of having a single node that does the detection and publishing all alone. In that case, the scaling and sensor configuration might be configured through rosparam.

dogoepp commented 8 years ago

I'm considering to write a second program mapping the ports with the serial numbers.

After a short investigation, I could find two options:

  1. a ROS node that searches for DAQs and provides a map from serial number to device full path (as a service or a topic)

    is portable but might not be flexible on the timing of connection of the sensors and/or their disconnection

  2. a script run by udev that names the device according to its serial number

    is probably more robust but requires udev (no Mac OS compatibility)

What way would you think to be more approriate ?

cc: @jbmouret

kirstyellis commented 8 years ago

@dogoepp, I think option 2 sounds like the best way to go, we are not concerned with Mac compatibility. Would you be able to provide a few more details about the work flow of this option. Ideally we would like the udev method to be optional so that if the udev rule is not installed, the user can provide a port name as before.

dogoepp commented 8 years ago

The udev approach allows to keep the ROS node simple. We only need to give it the path to the sensor, as it was done until now. The udev rule would ensure that we can use a reliable path, containing the serial number. I still have to check that it can work well.

Maybe an example can better illustrate the idea:

  1. on connection of the sensor, the udev rule calls a program that will get the device's serial number, say ISE177
  2. based on this information, udev creates the device /dev/optoforceISE177
  3. knowing the serial number of a sensor, we set the device path for the optoforce node to the value above and we are sure that it will always be the same sensor we talk to. In that case, we don't need to have the sensor's serial number in the topic's name.

In this case, the current master version of the node would work. However, I need to talk to the sensors to get its serial number. I would like to use the node's communication code to do so. So, if it's fine for you, I would make OptoforceDriver ROS-agnostic, adapt the node and create a second program that only gets the serial number, using this Optoforce class.

kirstyellis commented 8 years ago

sounds great!

dogoepp commented 8 years ago

Since a lot of work was done and I could not keep working on the serial_number branch as others used it, I had to create the new pull request #23. I close this one.