.NET coding patterns(davidfowl)
Posted 听雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET coding patterns(davidfowl)相关的知识,希望对你有一定的参考价值。
Table of contents
- Generics types as a factory
- Lazy initialization of services
- Single implementation multiple interfaces
- Creating instances of types from an IServiceProvider
- Caching singletons in generic types
- Resolving services when using IOptions<T>
Generics types as a factory
This pattern is used in Microsoft.Extensions.* and in Microsoft.AspNetCore.*. The idea is that you can use a generic type as a factory instead of a function. The type argument is the type you want to instantiate. Consider the example below where we have an IServiceFactory<TService>
that can resolve the TService
from the DI container or creates an instance if it\'s not in the container.
public interface IServiceFactory<TService> TService Service get; public class ServiceFactory<TService> : IServiceFactory<TService> public ServiceFactory(IServiceProvider service) Service = (TService)service.GetService(typeof(TService)) ?? ActivatorUtilities.CreateInstance<TService>(service); public TService Service get;
The constructor has a access to any service and the generic type being requested. These open generic services are registered like this:
public void ConfigureServices(IServiceCollection services) services.AddTransient(typeof(IServiceFactory<>), typeof(ServiceFactory<>));
Lazy initialization of services
Sometimes it\'s necessary to create services later than a constructor. The usually work around for this is to inject and IServiceProvider
into the constructor of the service that needs to lazily create the instance.
public class Service private readonly IServiceProvider _serviceProvider; public Service(IServiceProvider serviceProvider) _serviceProvider = serviceProvider; public IFoo CreateFoo() => _serviceProvider.GetRequiredService<IFoo>(); public IBar CreateBar() => _serviceProvider.GetRequiredService<IBar>();
If the types are known ahead of time, we can build a custom service provider lazy type to encapsulate the service locator pattern:
public interface ILazy<T> T Value get; public class LazyFactory<T> : ILazy<T> private readonly Lazy<T> _lazy; public LazyFactory(IServiceProvider service) _lazy = new Lazy<T>(() => service.GetRequiredService<T>()); public T Value => _lazy.Value; public class Service private readonly ILazy<IFoo> _foo; private readonly ILazy<IBar> _bar; public Service(ILazy<IFoo> foo, ILazy<IBar> bar) _foo = foo; _bar = bar; public IFoo CreateFoo() => _foo.Value; public IBar CreateBar() => _bar.Value;
Registered like this:
public void ConfigureServices(IServiceCollection services) // We register this as transient so it handles both singleton and scoped services // as the IServiceProvider injected will have the relevant lifetime. services.AddTransient(typeof(ILazy<>), typeof(LazyFactory<>));
Lazy<T>
could also be used as-is if added with a concrete type at service registration time:
public void ConfigureServices(IServiceCollection services) services.AddTransient<Lazy<IFoo>>(sp => new Lazy<IFoo>(() => sp.GetRequiredService<IFoo>()); services.AddTransient<Lazy<IBar>>(sp => new Lazy<IBar>(() => sp.GetRequiredService<IBar>());
Single implementation multiple interfaces
Let\'s say you had a type that implemented multiple interfaces and you wanted to expose it using the DI container. The built in IServiceCollection
type doesn\'t natively support this but it\'s easy to emulate using the following pattern.
public class FooAndBar : IFoo, IBar // Imagine a useful implementation
public void ConfigureServices(IServiceCollection services) services.AddSingleton<FooAndBar>(); services.AddSingleton<IFoo>(sp => sp.GetRequiredService<FooAndBar>()); services.AddSingleton<IBar>(sp => sp.GetRequiredService<FooAndBar>());
This will let me resolve FooAndBar
, IFoo
and IBar
and it will give me the same instance.
Creating instances of types from an IServiceProvider
Usually you need to register a type in order to instantiate instances of a type from the DI container, somebody needs to call IServiceProvider.GetService
. This means that the service needs to be registered in the container. This may not be desirable if these types are discovered dynamically (like controllers in MVC). There\'s a useful utility called ActivatorUtilities that can be used as a factory for types that haven\'t been registered, but have dependencies that are registered in the DI container.
public class MyDependency public MyDependency(ILogger logger)
public class MyDependencyFactory private readonly IServiceProvider _serviceProvider; public MyDependencyFactory(IServiceProvider serviceProvider) _serviceProvider = serviceProvider; public MyDependency GetInstance() return ActivatorUtilities.CreateInstance<MyDependency>(_serviceProvider);
You can build a more optimized version of this uses the CreateFactory
. This will pre-calculate the constructor based on the types passed and build a factory for it.
public class MyDependencyFactory private readonly IServiceProvider _serviceProvider; private readonly ObjectFactory _factory; public MyDependencyFactory(IServiceProvider serviceProvider) _serviceProvider = serviceProvider; _factory = ActivatorUtilities.CreateFactory(typeof(MyDependency), Type.EmptyTypes); public MyDependency GetInstance() return (MyDependency) _factory(_serviceProvider, null);
NOTE: Disposable instances created with this API will not be disposed by the DI container.
Caching singletons in generic types
If you need to cache an instance of something based on type, then you can store it on a static field of a generic type.
public class Factory public T Create<T>() where T : new() return Cache<T>.Instance; private static class Cache<T> where T : new() public static readonly T Instance = new();
You can use the JIT to cache instances on your behalf instead of a slower ConcurrentDictionary<Type, T>
. It can also be used to cache the expensive object creation process:
public class Factory public T Create<T>() where T : new() return Cache<T>.Instance; private static class Cache<T> where T : new() public static readonly T Instance = CallExpensiveMethodToBuildANewInstance(); private static T CallExpensiveMethodToBuildANewInstance() // Imagine a really complex process to construct T return instance.
Resolving services when using IOptions<T>
The options pattern is used in Microsoft.Extensions.* and in Microsoft.AspNetCore.*. It allows anyone to register a callback to mutate a POCO used to configure a library. Sometimes you\'d like access to services when configuring these options. There are multiple ways to do this:
public class LibraryOptions public int Setting get; set; public class MyConfigureOptions : IConfigureOptions<LibraryOptions> private readonly ISomeService _service; public MyConfigureOptions(ISomeService service) _service = service; public void Configure(LibraryOptions options) options.Setting = _service.ComputeSetting();
Followed by the registration.
public void ConfigureServices(IServiceCollection services) services.AddSingleton<IConfigureOptions<LibraryOptions>, MyConfigureOptions>();
That\'s a bit verbose, so we added some helpers to make it easier.
public void ConfigureServices(IServiceCollection services) services.AddOptions<LibraryOptions>() .Configure<ISomeService>((options, service) => options.Setting = service.ComputeSetting(); );
Asynchronous Socket Server
Below is a modern asynchronous server using modern .NET APIs:
- Task based APIs for asynchronous accept/read/write
- Range syntax for slicing the buffer
using var listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, 8080)); Console.WriteLine($"Listening on listenSocket.LocalEndPoint"); listenSocket.Listen(); while (true) // Wait for a new connection to arrive var connection = await listenSocket.AcceptAsync(); // We got a new connection spawn a task to so that we can echo the contents of the connection _ = Task.Run(async () => var buffer = new byte[4096]; try while (true) int read = await connection.ReceiveAsync(buffer, SocketFlags.None); if (read == 0) break; await connection.SendAsync(buffer[..read], SocketFlags.None); finally connection.Dispose(); );
Asynchronous Socket Client
Below is a modern asynchronous client using modern .NET APIs:
- Uses Task based APIs to establish the connection.
- Creates a
NetworkStream
over theSocket
in order to useCopyToAsync
, a helper that makes it easy to copy content from oneStream
to another.
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 8080)); Console.WriteLine("Type into the console to echo the contents"); var ns = new NetworkStream(socket); var readTask = Console.OpenStandardInput().CopyToAsync(ns); var writeTask = ns.CopyToAsync(Console.OpenStandardOutput()); // Quit if any of the tasks complete await Task.WhenAny(readTask, writeTask);
![](https://image.cha138.com/20230422/71257d29acb243b79e928f9911f3c66c.jpg)
ROS实验笔记之——基于kalibr来标定DAVIS346
之前博客《ROS学习笔记之——DAVIS346 calibration》已经实现了用dv-gui(Calibration [Tutorial] · DV)来标定event camera了。但是缺少了跟IMU的外参标定等等。本博文利用Kalibr库来对其进行标定。
Kalibr安装
先创建一个工作空间
mkdir -p ~/kalibr_workspace/src
cd ~/kalibr_workspace
source /opt/ros/melodic/setup.bash
catkin init
catkin config --extend /opt/ros/melodic
catkin config --merge-devel
catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release
cd ~/kalibr_workspace/src
git clone https://github.com/ethz-asl/Kalibr.git
cd ~/kalibr_workspace
catkin build -DCMAKE_BUILD_TYPE=Release -j4
可能会提示缺少libv4l,安装依赖包即可:
sudo apt-get install libv4l-dev
建立完之后需要source一下
source ~/kalibr_workspace/devel/setup.bash
还是没有办法。。。。还要把操蛋的opencv4安装了。。。。。
改为参考:melodic 安装 kalibr_matthewsyc的博客-CSDN博客
sudo apt-get install python-setuptools python-rosinstall ipython
sudo apt-get install libeigen3-dev libboost-all-dev doxygen
sudo apt-get install ros-melodic-cmake-modules python-software-properties
sudo apt-get install software-properties-common libpoco-dev python-matplotlib
sudo apt-get install python-scipy python-git python-pip ipython libtbb-dev
sudo apt-get install libblas-dev liblapack-dev python-catkin-tools libv4l-dev
sudo apt-get install python-igraph
安装完依赖后,编译好像就比较正常了~
每次运行前,记得加入
source ~/kalibr_workspace/devel/setup.bash
IMU标定
产生数据写入imu.yaml中,imu.yaml文件要用于联合标定。
imu_utils依赖code_utils,要先安装code_utils再安装imu_utils。
mkdir -p ~/imu_catkin_ws/src
cd ~/imu_catkin_ws/src
catkin_init_workspace
cd ..
catkin_make
source ~/imu_catkin_ws/devel/setup.bash
cd ~/imu_catkin_ws/src
git clone https://github.com/gaowenliang/code_utils.git
cd ..
catkin_make
可能出现报错,试试运行
sudo apt-get install libdw-dev
catkin_make时出现backward.hpp没有找到
解决方法:将sumpixel_test.cpp中# include "backward.hpp"改为:#include “code_utils/backward.hpp”
即可安装成功!
然后下载imu_utils
cd ~/imu_catkin_ws/src/
git clone https://github.com/gaowenliang/imu_utils.git
cd ..
catkin_make
然后运行
roslaunch vins davis_testing.launch
rostopic list
rostopic echo /dvs/imu
记录imu信息,这个过程要保持imu静止不动至少2个小时(这个太夸张了吧。。。)
17.26开始
在imu_utils文件下的launch文件目录下添加一个launch文件,我这里添加的是 imu.launch,并把以下代码复制进文件,代码中的/IMU_data改成自己imu的topic。
<launch>
<node pkg="imu_utils" type="imu_an" name="imu_an" output="screen">
<!--TOPIC名称-->
<param name="imu_topic" type="string" value= "/dvs/imu"/>
<!--imu_name 无所谓-->
<param name="imu_name" type="string" value= "imu_davis346"/>
<!--标定结果存放路径-->s
<param name="data_save_path" type="string" value= "$(find imu_utils)/data/"/>
<!--数据录制时间-min-->
<param name="max_time_min" type="int" value= "120"/>
<!--采样频率,即是IMU频率,采样频率可以使用rostopic hz /dvs/imu查看,设置为400,为后面的rosbag play播放频率-->
<param name="max_cluster" type="int" value= "400"/>
</node>
</launch>
运行之前要先source一下
source ~/imu_catkin_ws/devel/setup.bash
roslaunch imu_utils davis_imu.launch
rosbag play -r 400 /home/kwanwaipang/dataset/gwphku/hku_davis346_imu_2021-10-25-17-26-06.bag
rosbag play /home/kwanwaipang/dataset/gwphku/hku_davis346_imu_2021-10-25-17-26-06.bag
标定结束后在imu_catkin_ws/src/imu_utils/data中生成许多文件,其中(d435i_imu_param.yaml)imu_davis346_imu_param.yaml就是我们想要的结果,展示如下:
%YAML:1.0
---
type: IMU
name: imu_davis346
Gyr:
unit: " rad/s"
avg-axis:
gyr_n: 3.4207443164969696e-03
gyr_w: 5.0328372766535944e-05
x-axis:
gyr_n: 2.8679837408646372e-03
gyr_w: 5.2377301938209580e-05
y-axis:
gyr_n: 3.4204365908127145e-03
gyr_w: 5.9820318994959333e-05
z-axis:
gyr_n: 3.9738126178135566e-03
gyr_w: 3.8787497366438921e-05
Acc:
unit: " m/s^2"
avg-axis:
acc_n: 4.0577448874376872e-02
acc_w: 8.7349218314222493e-04
x-axis:
acc_n: 3.6370001351966601e-02
acc_w: 9.6556492723349487e-04
y-axis:
acc_n: 3.4926548351679493e-02
acc_w: 8.3124335009033201e-04
z-axis:
acc_n: 5.0435796919484514e-02
acc_w: 8.2366827210284801e-04
编写imu.yaml,格式参考https://github.com/ethz-asl/kalibr/wiki/yaml-formats中的imu.yaml,具体参数使用之前imu标定得到的参数,示例如下:
#Accelerometers
accelerometer_noise_density: 4.0577448874376872e-02 #Noise density (continuous-time)
accelerometer_random_walk: 8.7349218314222493e-04 #Bias random walk
#Gyroscopes
gyroscope_noise_density: 3.4207443164969696e-03 #Noise density (continuous-time)
gyroscope_random_walk: 5.0328372766535944e-05 #Bias random walk
rostopic: /dvs/imu #the IMU ROS topic
update_rate: 1000.0 #Hz (for discretization of the values above)
若不加速播放rosbag得到的结果如下:
%YAML:1.0
---
type: IMU
name: imu_davis346
Gyr:
unit: " rad/s"
avg-axis:
gyr_n: 3.4869979619824697e-03
gyr_w: 4.4740603706140853e-05
x-axis:
gyr_n: 2.9539477648808021e-03
gyr_w: 5.0580216740422281e-05
y-axis:
gyr_n: 3.5214141180930189e-03
gyr_w: 5.4806050604838655e-05
z-axis:
gyr_n: 3.9856320029735878e-03
gyr_w: 2.8835543773161611e-05
Acc:
unit: " m/s^2"
avg-axis:
acc_n: 4.2153479580575477e-02
acc_w: 8.5654389798364998e-04
x-axis:
acc_n: 3.7662088244236419e-02
acc_w: 8.9242080034822346e-04
y-axis:
acc_n: 3.8469169601091632e-02
acc_w: 8.9970652031743314e-04
z-axis:
acc_n: 5.0329180896398394e-02
acc_w: 7.7750437328529356e-04
还是有点差别的~按这个来把~~~
camera标定
产生数据文件也是用于联合标定
1.标定板,可在kalibr的wiki中下载,地址:https://github.com/ethz-asl/kalibr/wiki/downloads
选择
下载(然后发现根本下载不了。。。eth这群货真的是。。。。event camera的驱动弄得崩溃,连标定的扳子也搞???)链接: 百度网盘 请输入提取码 提取码: r4ts
然后缩放到40%,用A4纸就可以打印出来
原始pdf的格子参数是:
6*6的格子
大格子边长:5.5cm
小格子边长:1.65cm
小格子与大格子边长比例:0.3
调整后的格子参数是:
大格子边长:2.4cm
小格子边长:0.75cm
小格子与大格子边长比例:0.3125
但这只是理想情况,实际情况还得实际测量。
新建april_6x6_A4.yaml文件,格式参考上图的yaml,内容展示如下:
target_type: 'aprilgrid' #gridtype
tagCols: 6 #number of apriltags
tagRows: 6 #number of apriltags
tagSize: 0.024 #size of apriltag, edge to edge [m]
tagSpacing: 0.3125 #ratio of space between tags to tagSize
录制bag,录制bag 的同时,相机对准标定板,或是固定相机或是固定标定板,晃动另一个,动作不要太大,不要让相机看不清标定板(就争取把标定板晃动到过相机像素平面的每个地方)
注意:需要修改相机帧数(官方推荐是4Hz,尽管实际频率不完全准确,但是不影响结果)
kalibr在处理标定数据的时候要求频率不能太高,一般为4Hz,我们可以使用如下命令来更改topic的频率,实际上是将原来的topic以新的频率转成新的topic,实际测试infra1才是对应左目相机
roslaunch vins davis_testing.launch
rostopic list
rostopic hz /dvs/image_raw
rosrun topic_tools throttle messages /dvs/image_raw 4.0 /color
rostopic hz /color
roslaunch vins davis_raw_image_recording.launch
录制的topic就是转换频率后的topic
rosbag play /home/kwanwaipang/dataset/gwphku/hku_davis346_image_raw_imu_2021-10-26-14-31-10.bag
然后就使用上面安装好的Kalibr进行标定
source ~/kalibr_workspace/devel/setup.bash
kalibr_calibrate_cameras --target /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/april_6x6_A4.yaml --bag /home/kwanwaipang/dataset/gwphku/hku_davis346_image_raw_imu_2021-10-26-14-31-10.bag --models pinhole-equi --topics /color --show-extraction
其中–target …/yaml/april_6x6_A4.yaml是标定板的配置文件,注意如果选择棋格盘,注意targetCols和targetRows表示的是内侧角点的数量,不是格子数量。–bag multicameras_calibration.bag是录制的数据包,models pinhole-equi pinhole-equi pinhole-equi表示三个摄像头的相机模型和畸变模型(解释参考https://github.com/ethz-asl/kalibr/wiki/supported-models,根据需要选取), --topics /infra_left /infra_right /color表示三个摄像头对应的拍摄的数据话题,–bag-from-to 10 100表示处理bag中10-100秒的数据。–show-extraction表示显示检测特征点的过程,这些参数可以相应的调整。
kalibr_calibrate_cameras --target ../yaml/april_6x6_A4.yaml --bag multicameras_calibration.bag --models pinhole-equi pinhole-equi pinhole-equi --topics /infra_left /infra_right /color --bag-from-to 10 100 --show-extraction
出来的标定结果如下图所示
得到的结果如下所示
cam0:
cam_overlaps: []
camera_model: pinhole
distortion_coeffs: [0.023761891762741624, -0.6457616668645434, 2.070114304159524,
-2.4332476089446473]
distortion_model: equidistant
intrinsics: [333.6626192244177, 333.5395724565995, 162.74154656440015, 133.0594987830887]
resolution: [346, 260]
rostopic: /color
若改为降低速率前的topic效果如下:
source ~/kalibr_workspace/devel/setup.bash
kalibr_calibrate_cameras --target /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/april_6x6_A4.yaml --bag /home/kwanwaipang/dataset/gwphku/hku_davis346_image_raw_imu_2021-10-26-14-31-10.bag --models pinhole-equi --topics /dvs/image_raw --show-extraction
cam0:
cam_overlaps: []
camera_model: pinhole
distortion_coeffs: [0.007145831923439896, -0.4460587583609059, 1.2828141357453273,
-1.3797467604219862]
distortion_model: equidistant
intrinsics: [333.649413082246, 333.5165283699802, 162.51929394284377, 132.6341713511583]
resolution: [346, 260]
rostopic: /dvs/image_raw
编写camchain.yaml,格式参考Kalibr官方教程https://github.com/ethz-asl/kalibr/wiki/yaml-formats中的chain.yaml,具体的参数参考上面得到的yaml文件,没有的参数可以删除,最终结果示例如下:
cam0:
camera_model: pinhole
intrinsics: [333.649413082246, 333.5165283699802, 162.51929394284377, 132.6341713511583]
distortion_model: equidistant
distortion_coeffs: [0.007145831923439896, -0.4460587583609059, 1.2828141357453273, -1.3797467604219862]
T_cam_imu:
- [0.01779318, 0.99967549,-0.01822936, 0.07008565]
- [-0.9998017, 0.01795239, 0.00860714,-0.01771023]
- [0.00893160, 0.01807260, 0.99979678, 0.00399246]
- [0.0, 0.0, 0.0, 1.0]
timeshift_cam_imu: -8.121e-05
rostopic: /dvs/image_raw
resolution: [346, 260]
IMU与camera的联合标定
将之前矫正好的文件放好
source ~/kalibr_workspace/devel/setup.bash
kalibr_calibrate_imu_camera --bag [filename.bag] --cam [camchain.yaml] --imu [imu.yaml] --target [target.yaml] --bag-from-to 15 75 --show-extraction
kalibr_calibrate_imu_camera --bag /home/kwanwaipang/dataset/gwphku/hku_davis346_image_raw_imu_2021-10-26-14-31-10.bag --cam /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/camchain.yaml --imu /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/imu.yaml --target /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/april_6x6_A4.yaml --show-extraction
也有一种说法需要调整参数频率
注意选择的camera失真模型(https://github.com/ethz-asl/kalibr/wiki/supported-models)
roslaunch vins davis_testing.launch
rosrun topic_tools throttle messages /dvs/image_raw 20.0 /image_raw
rosrun topic_tools throttle messages /dvs/imu 200.0 /imu
roslaunch vins davis_raw_image_imu_recording.launch
source ~/kalibr_workspace/devel/setup.bash
kalibr_calibrate_cameras --target /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/april_6x6_A4.yaml --bag /home/kwanwaipang/dataset/gwphku/hku_davis346_image_imu_2021-10-26-16-14-23.bag --models pinhole-radtan --topics /image_raw --show-extraction
kalibr_calibrate_imu_camera --bag /home/kwanwaipang/dataset/gwphku/hku_davis346_image_imu_2021-10-26-16-14-23.bag --cam /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/camchain_second.yaml --imu /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/imu_second.yaml --target /home/kwanwaipang/catkin_ws_dvs/src/EVIO/config/kalibr_davis/april_6x6_A4.yaml --show-extraction
至此,矫正完成!测试一下这些参数。
出来的pdf report如下
camera校正的report
camera-imu校正的report
参考资料
GitHub - ethz-asl/kalibr: The Kalibr visual-inertial calibration toolbox
GitHub - gaowenliang/imu_utils: A ROS package tool to analyze the IMU performance.
联合标定双目相机和imu,使用工具Kalibr_甜甜圈吃不完的博客-CSDN博客
联合标定单目相机和imu,使用工具Kalibr_甜甜圈吃不完的博客-CSDN博客
realsenseD435i imu+双目标定_crazyfox的博客-CSDN博客_d435i imu标定
Installation · ethz-asl/kalibr Wiki · GitHubhttps://blog.csdn.net/Hanghang_/article/details/103546033Installation · ethz-asl/kalibr Wiki · GitHub
以上是关于.NET coding patterns(davidfowl)的主要内容,如果未能解决你的问题,请参考以下文章