roboception / rc_genicam_api

Roboception convenience layer around GenICam and GigE Vision.
Other
114 stars 45 forks source link

Unable to grab images from GenTL 1.5 producer #13

Closed spiderkeys closed 5 years ago

spiderkeys commented 5 years ago

I am working with a number of different GigE cameras, all of them being GenTL 1.5 producers (IDS, Sentech, etc) and was trying out rc_genicam_api with them. I ran into an issue with the Sentech models, where gc_stream doesn't seem to be able to receive any data from the camera. The strange thing is, I do see the data coming through on wireshark (as shown in the image below). It looks like the rcg::Stream::grab() method is timing out without receiving any events ("Cannot grab images" is emitted along the timeout period).

Screenshot from 2019-05-13 16-15-08 (ignore the package size: 2900, vs my wireshark payload sizes of 4014. I was playing around with MTU settings to see if they made a difference in this specific run)

I'm using the latest 2.2.0 release, and everything seems to work fine with all of my other cameras from other vendors. It may be worth noting that the Sentech camera is a Pleora device, which I noticed should be supported in this issue: https://github.com/roboception/rc_genicam_api/issues/8.

I've also made sure that the devices are on the same subnet and have used other GigE tools to access the streams of this camera to make sure it is working (aravis, eBUS SDK, IDS's Vision Cockpit, etc).

Any ideas what might be wrong? Hoping this might not be a camera specific issue. Thanks!

spiderkeys commented 5 years ago

Something additional that I just noticed, which may be directly causing this: The sentech camera seems to broadcast the video data to 0.0.0.0, whereas my other cameras send data directly to my host IP.

Checking the config of the device, I'm not sure why this is happening or if there is a GEV config parameter which controls this behavior.

From gc_config:

spiderkeys@spiderlair:~/workspace/rc_genicam_api/build$ ./tools/gc_config 18G3468
ID:                       IDS GigE Vision Ifc@D8-9E-F3-FA-03-29:2852042497:4294901760:18G3468
GenTL ID:                 SENTECH/STC_SCS231POE/18G3468
Serial number:            18G3468
User defined ID:          spider
MAC Address:              111cf9124d

Current IP:               a9feb70a
Current subnet mask:      ffff0000
Current gateway:          a9feb701

Persistent IP on/off:     1
  Persistent IP:          a9feb70a
  Persistent subnet mask: ffff0000
  Persistent gateway:     a9feb701
DHCP on/off:              0
Link local on/off:        1

PTP:                      
PTP status:

Host: 169.254.183.1 (0xa9feb701) Camera: 169.254.183.10 (0xa9feb701)

Additionally, when opening the camera with other GEV tools (aravis's arv-viewer and IDS vision cockpit), the camera sends data to the Host IP as well, rather than the broadcast address.

spiderkeys commented 5 years ago

Ended up figuring out the issue: the rcg::getDevices() method was just using using the first returned device, and if you have multiple .cti files for different types of cameras available in your GENICAM_GENTLX_PATH, it could select the wrong one and you end up with strange behavior. In the example above, it was finding the IDS camera's cti file first and trying to open the Sentech camera with that.

I've added a quick fix to my local branch that adds a filename argument to the getDevices() method and selects the CTI file based on that. You could also probably use the SystemID, if you desire. If this is of interest, I can open a PR.

Additional marginally related things I found while working on this problem:

heikohimu commented 5 years ago

Thanks for bringing this issue up.

I see the problem that rcg::getDevice(const char *) may discover the device with a wrong producer, if you have multiple producers for the same transport technology in your path. I thought that this is a rare case, but maybe I am wrong. However, you could also use rcg::getDevices() to get a list of all devices from all interfaces and systems and select the one that you want in your application, i.e. you can get to the parent interface and system with the getParent() methods in Device and Interface to check what system (i.e. producer) you want to use.

Regarding the timeout, you are talking about the IFUpdateDeviceList() call? Increasing this to 100 ms should not harm I guess. I can change that.

Regarding the colon in device IDs. Yes, I see the problem. We wanted to be able to specify the interface and device in one bigger ID, since with GigE Vision devices we often have the issue that we discover them via Ethernet and WLAN at the same time. So we need to ensure that we do not use WLAN. Using any other character may lead to the same problem. A quick workaround is to always specify the device ID with a colon. Using a leading colon without anything before has the same effect as not specifying the interface. It does not harm but prevents wrongly splitting the device ID if it contains colons.

spiderkeys commented 5 years ago

With many of these things patched up on my side now, things get a little bit more hairy w.r.t. to interface names. As shown below, the IDS camera interface IDs and display names apparently have spaces, @'s, dashes, and colons, which corroborates your point that changing it to a different character won't really help.

Transport Layer IDS GenICam Producer (GEV)
Vendor:         IDS Imaging Development Systems GmbH
Model:          IDS GenICam Producer (GEV)
Vendor version: 1.1.21.309
TL type:        GEV
Name:           ids_gevgentl.cti
Pathname:       /opt/genicam/lib/cti/ids_gevgentl.cti
Display name:   IDS GenICam Producer (GEV)
GenTL version   1.5

    Interface     IDS GigE Vision Ifc@D8-9E-F3-FA-03-29:2852042497:4294901760
    Display name: IDS GigE Vision @ "enxd89ef3fa0329" (D8-9E-F3-FA-03-29)
    TL type:      GEV

    Interface     IDS GigE Vision Ifc@9C-B6-D0-F9-41-D3:3232236395:4294967040
    Display name: IDS GigE Vision @ "wlp2s0" (9C-B6-D0-F9-41-D3)
    TL type:      GEV

    Interface     IDS GigE Vision Ifc@02-42-85-A0-3F-F1:2886795265:4294901760
    Display name: IDS GigE Vision @ "docker0" (02-42-85-A0-3F-F1)
    TL type:      GEV

Transport Layer E9981CF8-0B1B-4646-802D-0E745E0E123C
Vendor:         OMRON_SENTECH
Model:          GenTL
Vendor version: 1.5.25.181214.011S
TL type:        Mixed
Name:           libstgentl.cti
Pathname:       /opt/genicam/lib/cti/libstgentl.cti
Display name:   OMRON SENTECH GenTL
GenTL version   1.5

    Interface     00A12E97-6D6A-4166-8B7D-7BC325A7E54B
    Display name: USB3Vision
    TL type:      U3V

    Interface     02:42:85:a0:3f:f1
    Display name: docker0
    TL type:      GEV

    Interface     9c:b6:d0:f9:41:d3
    Display name: wlp2s0
    TL type:      GEV

    Interface     d8:9e:f3:fa:03:29
    Display name: enxd89ef3fa0329
    TL type:      GEV

Below is my currently implemented rcg::getDevices() method, which I had modified to use an @ until I saw the IDS values. Notice that I reordered the \<devid> relative to the \<interfid> and put the split token between the two, such that the command line arg looks something like:

./tools/gc_info -f /opt/genicam/lib/cti/ids_gevgentl.cti 4103293463@d8:9e:f3:fa:03:29
std::shared_ptr<Device> getDevice(const std::string& id, const std::string& tlpathname)
{
  std::shared_ptr<Device> ret;

  if (!id.empty())
  {
    // split into interface and device id (format: <devid>[@<interfid>])
    std::string interfid;
    std::string devid=id;

    size_t p=devid.find('@');
    if (p != std::string::npos)
    {
      devid=id.substr(0, p);
      interfid=id.substr(p+1);
    }

    // iterate through all systems
    for (auto& sys : rcg::System::getSystems())
    {
      sys->open();

      // filter on tl filename
      if (sys->getFilename()==tlpathname || tlpathname.empty() )
      {
        // iterate through all interfaces for system
        for (auto& interf : sys->getInterfaces())
        {
          // filter on interface argument
          if (interf->getID()==interfid || interfid.empty())
          {
            interf->open();
            ret=interf->getDevice(devid.c_str());
            interf->close();

            if (ret)
            {
              sys->close();
              return ret;
            }
          }
        }
      }
      sys->close();
    }
  }

  return ret;
}

What if the interface arg parsing was modified to support optional enclosure between two tokens (like {x}, "x" or something similar) such that complicated interface IDs like the IDS camera's could be captured in the following manner:

./tools/gc_info -f /opt/genicam/lib/cti/ids_gevgentl.cti 4103293463@{IDS GigE Vision Ifc@D8-9E-F3-FA-03-29:2852042497:4294901760}

Since the device ID is on the left, it doesn't matter if there are additional split tokens present in the interface string, or if the split token/interface name aren't specified at all, and it additionally could support escaped interface names, meeting all of the stated goals.

Some toy code as an example: https://wandbox.org/permlink/meViz1cDhMhAdSjv

heikohimu commented 5 years ago

I would like to avoid a complicated quoting syntax. I would rather introduce the possibility to specify the interface in a separate string (via a separate, optional parameter). If the interface is given (i.e. this string is not empty), our current mechanism that looks for a colon could be automatically disabled. In this way we could maintain compatibility, since we use the colon mechanism a lot, and handle complicated cases if needed. However, I will not be able to implement this right away. You are welcome to push a solution if you like to.

Regarding the -f parameter, we would rather avoid that. For our tools, it is easy to just redefine the GENICAM_GENTLX_PATH to include only the requested producer before executing the tool, which is effectively the same that is done by -f. Furthermore, for your own software, you have all possibilities in the API to just request devices from the system of your choice.

spiderkeys commented 5 years ago

:+1: Sounds good. At the least, just wanted to provide info on this use-case, in case anyone else ran into the CTI file issue. Agreed that much of this can be handled in the user app.

Thanks!