basler / pypylon

The official python wrapper for the pylon Camera Software Suite
http://www.baslerweb.com
BSD 3-Clause "New" or "Revised" License
570 stars 207 forks source link

Connecting to Gige camera via IP address broken on Ubuntu 18.01 and Centos #71

Open adragoset opened 5 years ago

adragoset commented 5 years ago

I've done some troubleshooting on Ubuntu 18.01 and Centos and I've found that attempting to connect and use a camera by specifying IP is broken and will timeout on RetrieveResult calls. This is true for both the Python and C++ API.

Wireshark session caps show the camera attempting to establish a udp session back to the host on 0.0.0.0:34950 which will not work since 0.0.0.0 isn't a real IP. Netcat on the host reports that the pylon driver opened up port 39450 for that session on the host adapters of which there are several.

untitled 192.168.3.209 is the camera. 192.168.3.219 is the host running pylon.

netcat output udp 0 0 0.0.0.0:34950 0.0.0.0:* 5414/./CalibrationT

I've had to roll all of my code back to grabbing the first device with a matching serial number.

To me it seems like something is being done to detect the interface on the host and that address is being reported incorrectly to the camera so it can never establish a proper udp session for data transfer.

Here is the c++ api code I've been testing with although this is reproducable with pypylon as well.

    TIFactory = &CTlFactory::GetInstance();
    pTI = TIFactory -> CreateTl(CBaslerGigEInstantCamera::DeviceClass());
    bdi = new CBaslerGigEDeviceInfo(pTI->CreateDeviceInfo());
    bdi->SetIpAddress(String_t(ip));
    std::shared_ptr<CBaslerGigEInstantCamera> camera1(new CBaslerGigEInstantCamera(pTI->CreateDevice((CDeviceInfo)*bdi), Cleanup_Delete));
    camera = camera1; 
thiesmoeller commented 5 years ago

Hi @adragoset,

on ubuntu 16.04 direct IP adressing works normally. Do find out what 18.04 has set differently and as you have the full pylon SDK installed, please check the README in /opt/pylon5. Could you check, if this option from the README helps?

adragoset commented 5 years ago

FIrewall was disabled entirely. This setup runs out of a docker container with --network=host setting on the container.

I will try those ip settings but im not really convinced this is a linux problem because grabbing works correctly when using the following c++ code in Ubuntu 18.01 or on my Centos 7 machines whereas the above posted code for finding a camera by IP remains broken.

Pylon::PylonInitialize();
TIFactory = &CTlFactory::GetInstance();
CDeviceInfo info;
info.SetSerialNumber(cameraSerial);
std::shared_ptr<CInstantCamera> camera_ptr(new CInstantCamera(TIFactory->CreateFirstDevice(info), Pylon::Cleanup_Delete));
camera = camera_ptr;
camera->Open();
GenApi::INodeMap& nodeMap = camera -> GetNodeMap();

Something specifically about creating the camera by setting the IP on CDeviceInfo breaks grabbing.

These are the values on my centos 7 (3.10.0-957.1.3.el7.x86_64) box for the adapter connected to the cameras network. Ill set these to ) as per above and try again when i have some time to mess with the IP code.

net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.enp7s0.rp_filter = 1
supershabam commented 5 years ago

I'm wondering if I was running into the same issue.

When connecting to a random device with the pypylon sample code such as grab.py I was able to capture images. But, when I attempt to attach to a specific IP address using the method described here https://github.com/basler/pypylon/issues/19 I am unable to pull an image.

I seem to have stumbled upon an undocumented way to select a specific device from the CreateFirstDevice method by passing in an info object. Using this approach, I am able to pull images.

from pypylon import pylon

ip_address = '192.168.1.14'
info = pylon.DeviceInfo()
info.SetPropertyValue('IpAddress', ip_address)

camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice(info))

# now use the camera like in grab.py https://github.com/basler/pypylon/blob/master/samples/grab.py

Maybe this info will be useful for you.

YacobBY commented 5 years ago

Hey I'm on Ubuntu 19.04 and I found a solution that worked for me:

Go to the internet settings of the ethernet adapter which your GigE camera is connected to and set both the IPv4 and IPv6 methods in their tabs to "local link only" option.

internetSettings

I also did what thiesmoeller said before. That didn't work by itself but it might be necessary along with this step.

ctyfang commented 4 years ago

Hey I'm on Ubuntu 19.04 and I found a solution that worked for me:

Go to the internet settings of the ethernet adapter which your GigE camera is connected to and set both the IPv4 and IPv6 methods in their tabs to "local link only" option.

internetSettings

I also did what thiesmoeller said before. That didn't work by itself but it might be necessary along with this step.

Thank you! This worked for me. I was really confused because I was able to find the camera on Windows, but not Ubuntu. Camera immediately showed up after I applied this.

petercheng00 commented 3 years ago

I'm having the exact same problem, using ubuntu 18.04. My laptop has a static ip, and the camera has a static ip as well. I'm using the C++ api though. If I instead use the commented out line, it captures fine.

ITransportLayer *pTL = CTlFactory::GetInstance().CreateTl(CBaslerGigEInstantCamera::DeviceClass());
CBaslerGigEDeviceInfo bdi(pTL->CreateDeviceInfo());
bdi.SetIpAddress(ip_address);
CBaslerGigEInstantCamera camera( pTL->CreateDevice( bdi ) );
// CBaslerGigEInstantCamera camera( pTL->CreateFirstDevice());
camera.Open();
camera.StartGrabbing();
camera.RetrieveResult(10, ptrGrabResult, TimeoutHandling_Return);
std::cout << ptrGrabResult << std::endl;

As a workaround, is there a way to list all cameras, and iterate through them, checking their ip addresses for a match? I haven't been able to find a good way to do that either.

IvanVishev commented 2 years ago

Hi @thiesmoeller! I am experiencing the same issue on Ubuntu Server 20.4 LTS. Suppose I connect to a specified IP Address, as my Server and Camera are in totally different subnets. In that case, the destination IP is set to 0.0.0.0.

I have tried out every proposed solution. Unfortunately, I don't even get the camera to be visible. Firewall is disabled (No FW on Network, also ufw on ubuntu is disabled). Here are the same issues which still not been resolved https://github.com/basler/pypylon/issues/132, https://github.com/basler/pypylon/issues/45 and some others. Changing camera.GevSCDA to host doesnot seem to change the interaface of the camera. With camera.GetDeviceInfo().GetInterface() i always get 0.0.0.0.

Everything works fine if I stay in the same subnet as the camera. Also, the interface is detected correctly.

Can you please provide some help on that issue? Thanks!

pjmara commented 2 years ago

@maximehyh If you have a global gIp that holds the camera IP you are looking for, you can use this utility function I wrote

    Pylon::CTlFactory& TlFactory = Pylon::CTlFactory::GetInstance();
    // This is a critical step- set the TL to BaslerGigEDevice class otherwise we won't find the cameras with enumerateDevices
    Pylon::ITransportLayer *pTl = TlFactory.CreateTl(Pylon::BaslerGigEDeviceClass);
    Pylon::DeviceInfoList_t lstDevices;
    pTl->EnumerateDevices( lstDevices );
    if ( lstDevices.empty() ) {

      printf("[BaslerEthernetDriver] No ethernet cameras found :(\n");
    }
    else
    {

      for(auto iter = lstDevices.begin(); iter != lstDevices.end(); iter++)
      {
            auto ipString = (*iter).GetIpAddress();

            //Compare the found IP to the one for this configured camera
            if(gIp.compare(ipString.c_str()) == 0)
            {
                gCamera = new Pylon::CInstantCamera(Pylon::CTlFactory::GetInstance().CreateDevice((*iter)));
                printf("[BaslerEthernetDriver] camera created! \n");
            }
      }
    }

I will delete my original comment- I made a mistake as to what specific step of my process ended up fixing the problem. It was changing the way I discovered and connected to the GigE device.

maximehyh commented 2 years ago

@pjmara thanks for your reply. I deleted my original message as my problem actually sits somewhere else. I am trying to get a stream from a Basler camera from within a docker container.

It is working fine if I set network: host in the docker configuration but I am having issues when trying not to use this network setup and trying to manually expose the UDP ports.

I know that upon connection the camera will negociate a UDP random port with my container and I believe it is possible to set/force this "random" port but did not find how to do it so far.

thiesmoeller commented 2 years ago

@maximehyh See https://github.com/basler/pypylon/issues/429

maximehyh commented 2 years ago

@thiesmoeller thanks for the quick answer. I managed to set the StreamGrabber port using this method and it is indeed working for the Grabbing part (I can see the port being used in Wireshark). Though it seems that my issue remains.

When using using docker without network: host, i.e pylon client being on a different network than the camera, it seems that I cannot manage to make the necessary steps to make a persistent connection to the camera that will then allow me to start the grabbing part (we are getting a No device attached error when trying to open()). Looking at Wireshark I am seeing that some other random UDP ports are used while connecting and I am wondering if that could be the issue.

Below are some wireshark screenshot and code snippets that could maybe help you understand our issue.

Question 1: would it be possible to set the port for this pre grabbing/connection process? I am thinking that maybe exposing those/this port on docker could fix the issue.

Question 2: I am seeing that some TCP connection is starting and failing when camera is without network: host. Maybe some failling step at a lower network level could cause the TCP connection to be tried (as the UDP one failed)?

FYI we did manage to make such a connection using Docker Swarm MacVLAN in the past but we would prefer not to use this network setup this time.

doConnection(const std::vector<std::string> &ip_list)
{
    // Cf https://www.baslerweb.com/en/sales-support/knowledge-base/frequently-asked-questions/how-can-i-create-a-gige-camera-device-object-with-a-known-ip-address/14991/
    CTlFactory& TIFactory = CTlFactory::GetInstance();
    ITransportLayer *pTI = TIFactory.CreateTl( Pylon::CBaslerGigEInstantCamera::DeviceClass() );

    try {
        for(int i = 0; i < ip_list.size(); ++i) {
            CBaslerGigEDeviceInfo bdi( pTI->CreateDeviceInfo() );
            bdi.SetIpAddress(ip_list[i].c_str());

            Pylon::CBaslerGigEInstantCamera ip_camera( pTI->CreateDevice( bdi ) );

                        ip_camera.Open();
        }
    } catch (GenICam_3_1_Basler_pylon_v5_1::RuntimeException &e) {
        std::ostringstream oss;
        oss << "[BaslerCamera] - " << e.what();
        throw std::runtime_error(oss.str());
    } catch (GenICam_3_1_Basler_pylon_v5_1::LogicalErrorException &e) {
        std::ostringstream oss;
        oss << "[BaslerCamera] - " << e.what();
        throw std::runtime_error(oss.str());
    } catch (GenICam_3_1_Basler_pylon_v5_1::AccessException &e) {
        std::ostringstream oss;
        oss << "[BaslerCamera] - " << e.what();
        throw std::runtime_error(oss.str());
    }
}

Result with network: host:

image

Without network: host:


# Error when trying to Open()
terminate called after throwing an instance of 'std::runtime_error'
  what():  [BaslerCameraContinuous] - No device attached. : RuntimeException thrown (file 'InstantCameraImpl.h', line 2077)

image

Thanks!

maximehyh commented 2 years ago

FYI made a separate issue here: https://github.com/basler/pypylon/issues/481

ghazalehtrb commented 2 years ago

I'm wondering if I was running into the same issue.

When connecting to a random device with the pypylon sample code such as grab.py I was able to capture images. But, when I attempt to attach to a specific IP address using the method described here #19 I am unable to pull an image.

I seem to have stumbled upon an undocumented way to select a specific device from the CreateFirstDevice method by passing in an info object. Using this approach, I am able to pull images.

from pypylon import pylon

ip_address = '192.168.1.14'
info = pylon.DeviceInfo()
info.SetPropertyValue('IpAddress', ip_address)

camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice(info))

# now use the camera like in grab.py https://github.com/basler/pypylon/blob/master/samples/grab.py

Maybe this info will be useful for you.

This solves the issue locally but doesn't seem to actually use the camera IP to connect as I'm not able to connect remotely. Using the original script below, I can connect to the camera but the frames are sent to 0.0.0.0. Is there any solution for this yet?

factory = pylon.TlFactory.GetInstance()
    ptl = factory.CreateTl('BaslerGigE')
    empty_camera_info = ptl.CreateDeviceInfo()
    empty_camera_info.SetPropertyValue('IpAddress', ip_address)
    camera_device = factory.CreateDevice(empty_camera_info)
    camera = pylon.InstantCamera(camera_device)
cpt-wojtech commented 11 months ago

The CBaslerGigEDeviceInfo, which is also created if we create a device info from the GigE specific Transportlayer, is deprecated alongside with other device specific info objects and camera devices since pylon 6.0 and shouldn't be used anymore. Obviously it has some issues. It's not recommended to use it anymore.

The general CDeviceInfo object works perfectly when creating a camera from a IP address.